diff --git a/AUTHORS b/AUTHORS
index face792d..bcc75b5 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -540,6 +540,7 @@
 Jinyoung Hur <hur.ims@navercorp.com>
 Jinyoung Hur <hurims@gmail.com>
 Jitendra Kumar Sahoo <jitendra.ks@samsung.com>
+Jitesh Pareek <j1.pareek@samsung.com>
 Joachim Bauch <jbauch@webrtc.org>
 Joachim Bauch <mail@joachim-bauch.de>
 Joanmarie Diggs <joanmarie.diggs@gmail.com>
diff --git a/DEPS b/DEPS
index 75dfc064..1c9c9a0 100644
--- a/DEPS
+++ b/DEPS
@@ -245,7 +245,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '2c3c3ca9524769b8f37458a0a41f18691787dfe5',
+  'skia_revision': 'bdc0bad2e216eab790842912ad184191c2426ac1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -253,7 +253,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': 'cb4f441c801f07e06b4b1e908c1ac3d471c96e75',
+  'angle_revision': 'a7e0d520f4ca8947444467424869f6a467f3acbc',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -261,7 +261,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
-  'pdfium_revision': 'b7a993309737db411ed3be04295f9797e765e645',
+  'pdfium_revision': '61bda438f9071586c92f8f626c29021524a8d0b0',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -360,7 +360,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': '5b95b128e760e27d272d205214bf7ead9a444aa5',
+  'dawn_revision': '3324caee9c7c34ac1bffef87a6ca378eb23b5150',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -404,7 +404,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.
-  'libcxxabi_revision':    '603d9d1067d178ee2dbd1ba8028413baa876a771',
+  'libcxxabi_revision':    '2715a6c0de8dac4c7674934a6b3d30ba0c685271',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -1609,7 +1609,7 @@
   'src/third_party/usrsctp/usrsctplib':
     Var('chromium_git') + '/external/github.com/sctplab/usrsctp' + '@' + '62d7d0c928c9a040dce96aa2f16c00e7e67d59cb',
 
-  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@4f48465a6a992770db56a19a885ff3407cde2193',
+  'src/third_party/vulkan-deps': '{chromium_git}/vulkan-deps@fbeca8f4ea6a6280982422028e991c2821385383',
 
   'src/third_party/vulkan_memory_allocator':
     Var('chromium_git') + '/external/github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git' + '@' + '5e49f57a6e71a026a54eb42e366de09a4142d24e',
@@ -1648,7 +1648,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + 'c843f8d63c8c17acfbb7d48e09059a581ba779b9',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '6e0209a50e86538338f69c558fe3d3ff3288d369',
+    Var('webrtc_git') + '/src.git' + '@' + '2be1808023189ba27973a1f0fc437125fbf5da77',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1730,7 +1730,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b7e78feca46940b0cda7f74b6dc0dae77da39e44',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@662e18c549447c9b9ea21f88058ed54842cc993a',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index 647d435..b547916 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -172,6 +172,7 @@
                   '|chromeos/assistant/'\
                   '|chromeos/services/assistant/'\
                   '|chromeos/services/libassistant/'\
+                  '|chrome/browser/ash/wallpaper_handlers/'\
                   '|chrome/browser/ash/web_applications/personalization_app/'\
                   '|chrome/browser/resources/settings/chromeos/ambient_mode_page/'\
                   '|chrome/browser/ui/ash/.*wallpaper.*/'\
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 28379e95..c29ba36 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -7,6 +7,7 @@
 import("//android_webview/variables.gni")
 import("//android_webview/webview_repack_locales.gni")
 import("//build/android/resource_sizes.gni")
+import("//build/config/android/channel.gni")
 import("//build/config/android/config.gni")
 import("//build/config/android/rules.gni")
 import("//build/config/locales.gni")
@@ -894,6 +895,13 @@
   if (webview_includes_weblayer) {
     sources += [ "$root_gen_dir/weblayer/weblayer_resources.pak" ]
     deps += [ "//weblayer:resources" ]
+
+    # These resources are primarily useful for developers. To avoid increasing
+    # the size of WebView they are only included in non-stable channels.
+    if (android_channel != "stable") {
+      sources += [ "$root_gen_dir/content/dev_ui_content_resources.pak" ]
+      deps += [ "//content:dev_ui_content_resources" ]
+    }
   }
 }
 
diff --git a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
index e052402..dad6617 100644
--- a/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
+++ b/android_webview/java/src/org/chromium/android_webview/AwWebContentsDelegateAdapter.java
@@ -245,10 +245,20 @@
     @Override
     public void navigationStateChanged(int flags) {
         if ((flags & InvalidateTypes.URL) != 0
-                && mAwContents.isPopupWindow()
-                && mAwContents.hasAccessedInitialDocument()) {
-            // Hint the client to show the last committed url, as it may be unsafe to show
-            // the pending entry.
+                && (flags
+                        != InvalidateTypes.ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY)
+                && mAwContents.isPopupWindow() && mAwContents.hasAccessedInitialDocument()) {
+            // This is a popup whose document has been accessed by script. Hint
+            // the client to show the last committed url, as it may be unsafe to
+            // show the pending entry. Note that we are also preserving old
+            // behavior by not firing this for a few cases where the navigation
+            // used to get "ignored" and won't trigger a NavigationStateChanged
+            // call.
+            // TODO(https://crbug.com/1277414): Figure out if it's possible to
+            // synthesize page load in this case, since it seems to be better
+            // to also show the last committed URL in the "used-to-be-ignored"
+            // cases too, probably by gating this behavior behind a flag and
+            // setting it based on targetSdkVersion.
             String url = mAwContents.getLastCommittedUrl();
             url = TextUtils.isEmpty(url) ? ContentUrlConstants.ABOUT_BLANK_DISPLAY_URL : url;
             mContentsClient.getCallbackHelper().postSynthesizedPageLoadingForUrlBarUpdate(url);
diff --git a/ash/BUILD.gn b/ash/BUILD.gn
index f25beff..ad8767b 100644
--- a/ash/BUILD.gn
+++ b/ash/BUILD.gn
@@ -2101,7 +2101,6 @@
     "//components/media_message_center",
     "//components/onc",
     "//components/password_manager/core/browser:hash_password_manager",
-    "//components/policy/core/browser",
     "//components/pref_registry",
     "//components/prefs",
     "//components/quirks",
@@ -2112,7 +2111,6 @@
     "//components/soda:constants",
     "//components/strings",
     "//components/sync",
-    "//components/url_matcher",
     "//components/user_manager",
     "//components/vector_icons",
     "//components/viz/host",
@@ -2425,7 +2423,6 @@
     "rotator/screen_rotation_animation_unittest.cc",
     "rotator/screen_rotation_animator_unittest.cc",
     "screen_util_unittest.cc",
-    "session/fullscreen_controller_unittest.cc",
     "session/fullscreen_notification_bubble_unittest.cc",
     "session/session_controller_impl_unittest.cc",
     "shelf/assistant_overlay_unittest.cc",
diff --git a/ash/DEPS b/ash/DEPS
index f07d3bc..4fedb476 100644
--- a/ash/DEPS
+++ b/ash/DEPS
@@ -13,7 +13,6 @@
   "+components/language/core/browser/pref_names.h",
   "+components/live_caption",
   "+components/media_message_center",
-  "+components/policy/core/browser",
   "+components/pref_registry",
   "+components/prefs",
   "+components/quirks",
@@ -22,7 +21,6 @@
   "+components/strings",
   "+components/sync",
   "+components/ui_devtools",
-  "+components/url_matcher",
   "+components/vector_icons",
   "+components/viz/common",
   "+components/viz/host",
diff --git a/ash/app_list/app_list_presenter_unittest.cc b/ash/app_list/app_list_presenter_unittest.cc
index c8ddb4a..49fe1595 100644
--- a/ash/app_list/app_list_presenter_unittest.cc
+++ b/ash/app_list/app_list_presenter_unittest.cc
@@ -213,6 +213,15 @@
     UpdateDisplay("1024x768");
   }
 
+  // Ensures the launcher is visible and showing the apps grid.
+  void EnsureLauncherWithVisibleAppsGrid() {
+    auto* helper = GetAppListTestHelper();
+    helper->ShowAndRunLoop(GetPrimaryDisplayId());
+    if (!features::IsProductivityLauncherEnabled())
+      helper->GetAppListView()->SetState(AppListViewState::kFullscreenAllApps);
+    helper->WaitUntilIdle();
+  }
+
   void SetAppListStateAndWait(AppListViewState new_state) {
     GetAppListView()->SetState(new_state);
     GetAppListTestHelper()->WaitUntilIdle();
@@ -225,23 +234,26 @@
   // Whether to run the test with fullscreen or not.
   bool TestFullscreenParam() const { return GetParam(); }
 
+  SearchBoxView* GetSearchBoxView() {
+    if (features::IsProductivityLauncherEnabled() &&
+        !Shell::Get()->IsInTabletMode()) {
+      DCHECK(Shell::Get()->app_list_controller()->IsVisible());
+      return GetAppListTestHelper()->GetBubbleSearchBoxView();
+    }
+    return GetAppListTestHelper()->GetSearchBoxView();
+  }
+
   gfx::Point GetPointOutsideSearchbox() {
     // Ensures that the point satisfies the following conditions:
     // (1) The point is within AppListView.
     // (2) The point is outside of the search box.
     // (3) The touch event on the point should not be consumed by the handler
     // for back gesture.
-    return GetAppListView()
-        ->search_box_view()
-        ->GetBoundsInScreen()
-        .bottom_right();
+    return GetSearchBoxView()->GetBoundsInScreen().bottom_right();
   }
 
   gfx::Point GetPointInsideSearchbox() {
-    return GetAppListView()
-        ->search_box_view()
-        ->GetBoundsInScreen()
-        .CenterPoint();
+    return GetSearchBoxView()->GetBoundsInScreen().CenterPoint();
   }
 
   AppListView* GetAppListView() {
@@ -256,11 +268,10 @@
   }
 
   AppsGridView* apps_grid_view() {
-    return GetAppListView()
-        ->app_list_main_view()
-        ->contents_view()
-        ->apps_container_view()
-        ->apps_grid_view();
+    if (features::IsProductivityLauncherEnabled())
+      return GetAppListTestHelper()->GetScrollableAppsGridView();
+
+    return GetAppListTestHelper()->GetRootPagedAppsGridView();
   }
 
   SearchResultBaseView* GetSearchResultListViewItemAt(int index) {
@@ -297,10 +308,8 @@
   // Returns the |dialog| vertical offset from the top of the search box bounds.
   int GetSearchResultsAnchoredDialogTopOffset(const views::Widget* dialog) {
     const gfx::Rect dialog_bounds = dialog->GetWindowBoundsInScreen();
-    const gfx::Rect search_box_bounds = GetAppListView()
-                                            ->search_box_view()
-                                            ->GetWidget()
-                                            ->GetWindowBoundsInScreen();
+    const gfx::Rect search_box_bounds =
+        GetSearchBoxView()->GetWidget()->GetWindowBoundsInScreen();
     return dialog_bounds.y() - search_box_bounds.y();
   }
 };
@@ -2314,7 +2323,9 @@
 }
 
 // Tests that the app list is not draggable in side shelf alignment.
-TEST_P(AppListPresenterTest, SideShelfAlignmentDragDisabled) {
+// TODO(crbug.com/1281927): Figure out if ProductivityLauncher needs to
+// support swipe to open and close.
+TEST_P(AppListPresenterNonBubbleTest, SideShelfAlignmentDragDisabled) {
   SetShelfAlignment(ShelfAlignment::kRight);
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   const AppListView* app_list = GetAppListView();
@@ -2451,7 +2462,7 @@
 }
 
 // Tests that the app list view handles drag properly in laptop mode.
-TEST_P(AppListPresenterTest, AppListViewDragHandler) {
+TEST_P(AppListPresenterNonBubbleTest, AppListViewDragHandler) {
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
 
@@ -2712,8 +2723,8 @@
   GetAppListTestHelper()->CheckVisibility(false);
 }
 
-// TODO(crbug.com/1273162): Fix for ProductivityLauncher.
-TEST_F(AppListPresenterTest, LongPressOutsideCloseAppList) {
+// ProductivityLauncher closes on touch-press, so this test isn't relevant.
+TEST_F(AppListPresenterNonBubbleTest, LongPressOutsideCloseAppList) {
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   GetAppListTestHelper()->CheckVisibility(true);
 
@@ -2730,8 +2741,8 @@
   GetAppListTestHelper()->CheckVisibility(false);
 }
 
-// TODO(crbug.com/1273162): Fix for ProductivityLauncher.
-TEST_F(AppListPresenterTest, TwoFingerTapOutsideCloseAppList) {
+// ProductivityLauncher closes on touch-press, so this test isn't relevant.
+TEST_F(AppListPresenterNonBubbleTest, TwoFingerTapOutsideCloseAppList) {
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   GetAppListTestHelper()->CheckVisibility(true);
 
@@ -2905,8 +2916,9 @@
 }
 
 // Tests that search box gets deactivated if the active search model gets
-// switched.
-TEST_P(AppListPresenterTest, SearchBoxDeactivatedOnModelChange) {
+// switched. Does not apply to ProductivityLauncher, where the search box is
+// always active.
+TEST_P(AppListPresenterNonBubbleTest, SearchBoxDeactivatedOnModelChange) {
   EnableTabletMode(true);
 
   const bool test_mouse_event = TestMouseEventParam();
@@ -2943,7 +2955,7 @@
 
 // Tests that search UI gets closed if search model gets changed.
 // TODO(crbug.com/1273162): Fix for ProductivityLauncher enabled.
-TEST_F(AppListPresenterTest, SearchClearedOnModelChange) {
+TEST_F(AppListPresenterNonBubbleTest, SearchClearedOnModelChange) {
   EnableTabletMode(true);
 
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
@@ -3270,9 +3282,10 @@
 
 // Tests that a drag to the bezel from Fullscreen/Peeking will close the app
 // list.
-// TODO(jamescook): Figure out if ProductivityLauncher needs to support swipe
-// to close.
-TEST_P(AppListPresenterTest, DragToBezelClosesAppListFromFullscreenAndPeeking) {
+// TODO(crbug.com/1281927): Figure out if ProductivityLauncher needs to
+// support swipe to open and close.
+TEST_P(AppListPresenterNonBubbleTest,
+       DragToBezelClosesAppListFromFullscreenAndPeeking) {
   const bool test_fullscreen = TestFullscreenParam();
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   AppListView* view = GetAppListView();
@@ -3302,9 +3315,9 @@
 
 // Tests that a drag to the bezel from Fullscreen/Peeking will close the app
 // list even on external display with non zero y origin.
-// TODO(jamescook): Figure out if ProductivityLauncher needs to support swipe
-// to close.
-TEST_P(AppListPresenterTest,
+// TODO(crbug.com/1281927): Figure out if ProductivityLauncher needs to
+// support swipe to open and close.
+TEST_P(AppListPresenterNonBubbleTest,
        DragToBezelClosesAppListFromFullscreenAndPeekingOnExternal) {
   UpdateDisplay("800x600,1000x768");
 
@@ -3343,7 +3356,6 @@
 }
 
 // Regression test for crash due to use-after-free. https://crbug.com/1163332
-// TODO(crbug.com/1273162): Fix for ProductivityLauncher.
 TEST_P(AppListPresenterTest, ShouldNotCrashOnItemClickAfterMonitorDisconnect) {
   // Set up two displays.
   UpdateDisplay("1024x768,1200x900");
@@ -3363,10 +3375,8 @@
   // Disconnect secondary display.
   UpdateDisplay("1024x768");
 
-  // Open app list to fullscreen.
-  helper->ShowAndRunLoop(GetPrimaryDisplayId());
-  helper->GetAppListView()->SetState(AppListViewState::kFullscreenAllApps);
-  helper->WaitUntilIdle();
+  // Open app list to show app items.
+  EnsureLauncherWithVisibleAppsGrid();
 
   // Click on an item.
   AppListItemView* item_view = apps_grid_view()->GetItemViewAt(0);
@@ -3579,7 +3589,8 @@
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
 }
 
-TEST_P(AppListPresenterTest, LongUpwardDragInFullscreenShouldNotClose) {
+TEST_P(AppListPresenterNonBubbleTest,
+       LongUpwardDragInFullscreenShouldNotClose) {
   const bool test_fullscreen_search = TestFullscreenParam();
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   AppListView* view = GetAppListView();
@@ -3644,8 +3655,9 @@
 
 // Tests closing the app list during drag, and verifies that drag updates are
 // ignored while the app list is closing.
-// TODO(crbug.com/1273162): Fix for ProductivityLauncher.
-TEST_P(AppListPresenterTest, DragUpdateWhileAppListClosing) {
+// TODO(crbug.com/1281927): Figure out if ProductivityLauncher needs to
+// support swipe to open and close.
+TEST_P(AppListPresenterNonBubbleTest, DragUpdateWhileAppListClosing) {
   const bool test_mouse_event = TestMouseEventParam();
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
   GetAppListTestHelper()->CheckState(AppListViewState::kPeeking);
@@ -5389,8 +5401,8 @@
 
 // Tests app list visibility when switching to tablet mode during dragging from
 // shelf.
-// TODO(jamescook): Figure out if ProductivityLauncher needs to support swipe
-// to open and close.
+// TODO(crbug.com/1281927): Figure out if ProductivityLauncher needs to
+// support swipe to open and close.
 TEST_F(AppListPresenterHomeLauncherTest,
        SwitchToTabletModeDuringDraggingFromShelf) {
   UpdateDisplay("1080x900");
@@ -5434,8 +5446,8 @@
 
 // Tests app list visibility when switching to tablet mode during dragging to
 // close app list.
-// TODO(jamescook): Figure out if ProductivityLauncher needs to support swipe
-// to open and close.
+// TODO(crbug.com/1281927): Figure out if ProductivityLauncher needs to
+// support swipe to open and close.
 TEST_F(AppListPresenterHomeLauncherTest,
        SwitchToTabletModeDuringDraggingToClose) {
   UpdateDisplay("1080x900");
@@ -5573,13 +5585,13 @@
   // FULLSCREEN_SEARCH.
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenSearch);
-  EXPECT_TRUE(GetAppListView()->search_box_view()->is_search_box_active());
+  EXPECT_TRUE(GetSearchBoxView()->is_search_box_active());
 
   // Tap or click the body of the AppList again, the searchbox should deactivate
   // and the applist should be in FULLSCREEN_ALL_APPS.
   ClickOrTap(GetPointOutsideSearchbox());
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
-  EXPECT_FALSE(GetAppListView()->search_box_view()->is_search_box_active());
+  EXPECT_FALSE(GetSearchBoxView()->is_search_box_active());
 }
 
 // Tests that tapping or clicking the body of the applist with an active virtual
@@ -5605,7 +5617,7 @@
   GetAppListTestHelper()->WaitUntilIdle();
   GetAppListTestHelper()->CheckState(AppListViewState::kFullscreenAllApps);
   EXPECT_FALSE(keyboard_controller->IsKeyboardVisible());
-  EXPECT_FALSE(GetAppListView()->search_box_view()->is_search_box_active());
+  EXPECT_FALSE(GetSearchBoxView()->is_search_box_active());
 }
 
 TEST_F(AppListPresenterHomeLauncherTest, TapHomeButtonOnExternalDisplay) {
@@ -5630,18 +5642,15 @@
 
 // Test that gesture tapping the app list search box correctly handles the event
 // by moving the textfield's cursor to the tapped position within the text.
-// TODO(crbug.com/1273162): Fix for ProductivityLauncher.
 TEST_P(AppListPresenterTest, SearchBoxTextfieldGestureTap) {
   GetAppListTestHelper()->ShowAndRunLoop(GetPrimaryDisplayId());
-  GetAppListView()->SetState(AppListViewState::kFullscreenAllApps);
 
   // Tap on the search box to focus and open it.
   ui::test::EventGenerator* generator = GetEventGenerator();
   generator->GestureTapAt(GetPointInsideSearchbox());
 
   // Set the text of the search box textfield.
-  views::Textfield* textfield =
-      GetAppListView()->search_box_view()->search_box();
+  views::Textfield* textfield = GetSearchBoxView()->search_box();
   textfield->SetText(u"Test search box string");
 
   // The textfield's cursor position should start out at the end of the string.
diff --git a/ash/app_list/views/paged_apps_grid_view.cc b/ash/app_list/views/paged_apps_grid_view.cc
index 42d647a..74a2e8ee 100644
--- a/ash/app_list/views/paged_apps_grid_view.cc
+++ b/ash/app_list/views/paged_apps_grid_view.cc
@@ -1417,7 +1417,9 @@
         std::ceil((available_height + kMaxVerticalPaddingBetweenTiles) /
                   (tile_height + kMaxVerticalPaddingBetweenTiles));
   }
-  return final_row_count;
+  // Unit tests may create artificially small screens resulting in
+  // `final_row_count` of 0. Return 1 row to avoid divide-by-zero in layout.
+  return std::max(final_row_count, 1);
 }
 
 int PagedAppsGridView::GetTotalTopPaddingOnFirstPage() const {
diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd
index 0179a45..6e70c9e 100644
--- a/ash/ash_strings.grd
+++ b/ash/ash_strings.grd
@@ -4383,6 +4383,9 @@
       <message name="IDS_AUTH_FACTOR_LABEL_UNLOCKED" desc="Label on user's lock screen when a Chromebook is successfully unlocked.">
         Unlocked
       </message>
+      <message name="IDS_AUTH_FACTOR_LABEL_SIGNED_IN" desc="Label on user's login screen when a Chromebook is successfully logged in.">
+        Signed in
+      </message>
 
       <!-- Launcher: Continue Section -->
       <message name="IDS_ASH_LAUNCHER_CONTINUE_SECTION_LABEL" desc="Label for the continue section of the launcher, which shows recent files and apps that the user can continue using.">
diff --git a/ash/ash_strings_grd/IDS_AUTH_FACTOR_LABEL_SIGNED_IN.png.sha1 b/ash/ash_strings_grd/IDS_AUTH_FACTOR_LABEL_SIGNED_IN.png.sha1
new file mode 100644
index 0000000..b4d40db
--- /dev/null
+++ b/ash/ash_strings_grd/IDS_AUTH_FACTOR_LABEL_SIGNED_IN.png.sha1
@@ -0,0 +1 @@
+d40f74374e01fb1984ddbe85d30449f80fe990fc
\ No newline at end of file
diff --git a/ash/capture_mode/capture_mode_unittests.cc b/ash/capture_mode/capture_mode_unittests.cc
index 4a1faf0..95b6257 100644
--- a/ash/capture_mode/capture_mode_unittests.cc
+++ b/ash/capture_mode/capture_mode_unittests.cc
@@ -37,6 +37,7 @@
 #include "ash/display/output_protection_delegate.h"
 #include "ash/display/screen_orientation_controller_test_api.h"
 #include "ash/display/window_tree_host_manager.h"
+#include "ash/projector/projector_annotation_tray.h"
 #include "ash/projector/projector_controller_impl.h"
 #include "ash/projector/test/mock_projector_client.h"
 #include "ash/public/cpp/capture_mode/capture_mode_test_api.h"
@@ -91,6 +92,7 @@
 #include "ui/compositor/layer.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/display/types/display_constants.h"
+#include "ui/events/event_handler.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/gfx/geometry/dip_util.h"
 #include "ui/gfx/geometry/insets.h"
@@ -5211,6 +5213,78 @@
   VerifyOverlayWindow(overlay_window, capture_source);
 }
 
+namespace {
+
+// Defines a class that intercepts the events at the post-target handling phase
+// and caches the last event target to which the event was routed.
+class EventTargetCatcher : public ui::EventHandler {
+ public:
+  EventTargetCatcher() {
+    Shell::GetPrimaryRootWindow()->AddPostTargetHandler(this);
+  }
+  EventTargetCatcher(const EventTargetCatcher&) = delete;
+  EventTargetCatcher& operator=(const EventTargetCatcher&) = delete;
+  ~EventTargetCatcher() override {
+    Shell::GetPrimaryRootWindow()->RemovePostTargetHandler(this);
+  }
+
+  ui::EventTarget* last_event_target() { return last_event_target_; }
+
+  // ui::EventHandler:
+  void OnEvent(ui::Event* event) override {
+    ui::EventHandler::OnEvent(event);
+    last_event_target_ = event->target();
+  }
+
+ private:
+  ui::EventTarget* last_event_target_ = nullptr;
+};
+
+}  // namespace
+
+TEST_F(ProjectorCaptureModeIntegrationTests, RecordingOverlayWidgetTargeting) {
+  auto* controller = CaptureModeController::Get();
+  controller->SetSource(CaptureModeSource::kFullscreen);
+  StartProjectorModeSession();
+  EXPECT_TRUE(controller->IsActive());
+
+  PressAndReleaseKey(ui::VKEY_RETURN);
+  WaitForRecordingToStart();
+  CaptureModeTestApi test_api;
+  RecordingOverlayController* overlay_controller =
+      test_api.GetRecordingOverlayController();
+
+  auto* projector_controller = ProjectorControllerImpl::Get();
+  projector_controller->OnMarkerPressed();
+  EXPECT_TRUE(overlay_controller->is_enabled());
+  auto* overlay_window = overlay_controller->GetOverlayNativeWindow();
+  VerifyOverlayEnabledState(overlay_window, /*overlay_enabled_state=*/true);
+
+  // Clicking anywhere outside the projector shelf pod should be targeted to the
+  // overlay widget window.
+  EventTargetCatcher event_target_catcher;
+  auto* event_generator = GetEventGenerator();
+  event_generator->set_current_screen_location(gfx::Point(10, 10));
+  event_generator->ClickLeftButton();
+  EXPECT_EQ(overlay_window, event_target_catcher.last_event_target());
+
+  // Now move the mouse over the projector shelf pod, the overlay should not
+  // consume the event, and it should instead go through to that pod.
+  auto* root_window = Shell::GetPrimaryRootWindow();
+  ProjectorAnnotationTray* annotations_tray =
+      RootWindowController::ForWindow(root_window)
+          ->GetStatusAreaWidget()
+          ->projector_annotation_tray();
+  EXPECT_TRUE(annotations_tray->visible_preferred());
+  event_generator->MoveMouseTo(
+      annotations_tray->GetBoundsInScreen().CenterPoint());
+  EXPECT_EQ(annotations_tray->GetWidget()->GetNativeWindow(),
+            event_target_catcher.last_event_target());
+
+  // The overlay status hasn't changed.
+  VerifyOverlayEnabledState(overlay_window, /*overlay_enabled_state=*/true);
+}
+
 // Tests that neither preview notification nor recording in tote is shown if in
 // projector mode.
 TEST_P(ProjectorCaptureModeIntegrationTests,
diff --git a/ash/capture_mode/recording_overlay_controller.cc b/ash/capture_mode/recording_overlay_controller.cc
index 7364d53..7732957d4 100644
--- a/ash/capture_mode/recording_overlay_controller.cc
+++ b/ash/capture_mode/recording_overlay_controller.cc
@@ -5,12 +5,17 @@
 #include "ash/capture_mode/recording_overlay_controller.h"
 
 #include "ash/capture_mode/capture_mode_controller.h"
+#include "ash/projector/projector_annotation_tray.h"
 #include "ash/public/cpp/capture_mode/recording_overlay_view.h"
 #include "ash/public/cpp/shell_window_ids.h"
+#include "ash/root_window_controller.h"
+#include "ash/system/status_area_widget.h"
 #include "ui/aura/window.h"
+#include "ui/aura/window_targeter.h"
 #include "ui/compositor/layer_type.h"
 #include "ui/gfx/geometry/rect.h"
 #include "ui/views/widget/widget.h"
+#include "ui/wm/core/coordinate_conversion.h"
 
 namespace ash {
 
@@ -53,6 +58,54 @@
              : window_being_recorded;
 }
 
+// Defines a window targeter that will be installed on the overlay widget's
+// window so that we can allow located events over the projector shelf pod or
+// its associated bubble widget to go through and not be consumed by the
+// overlay. This enables the user to interact with the annotation tools while
+// annotating.
+class OverlayTargeter : public aura::WindowTargeter {
+ public:
+  explicit OverlayTargeter(aura::Window* overlay_window)
+      : overlay_window_(overlay_window) {}
+  OverlayTargeter(const OverlayTargeter&) = delete;
+  OverlayTargeter& operator=(const OverlayTargeter&) = delete;
+  ~OverlayTargeter() override = default;
+
+  // aura::WindowTargeter:
+  ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
+                                      ui::Event* event) override {
+    if (event->IsLocatedEvent()) {
+      auto* root_window = overlay_window_->GetRootWindow();
+      ProjectorAnnotationTray* annotations =
+          RootWindowController::ForWindow(root_window)
+              ->GetStatusAreaWidget()
+              ->projector_annotation_tray();
+      if (annotations && annotations->visible_preferred()) {
+        auto* located_event = event->AsLocatedEvent();
+        auto screen_location = located_event->root_location();
+        wm::ConvertPointToScreen(root_window, &screen_location);
+
+        // Let events over the projector shelf pod to go through.
+        if (annotations->GetBoundsInScreen().Contains(screen_location))
+          return nullptr;
+
+        // Let events over the projector bubble widget (if shown) to go through.
+        views::Widget* bubble_widget = annotations->GetBubbleWidget();
+        if (bubble_widget && bubble_widget->IsVisible() &&
+            bubble_widget->GetWindowBoundsInScreen().Contains(
+                screen_location)) {
+          return nullptr;
+        }
+      }
+    }
+
+    return aura::WindowTargeter::FindTargetForEvent(root, event);
+  }
+
+ private:
+  aura::Window* const overlay_window_;
+};
+
 }  // namespace
 
 RecordingOverlayController::RecordingOverlayController(
@@ -76,6 +129,9 @@
   overlay_widget_->Init(std::move(params));
   recording_overlay_view_ = overlay_widget_->SetContentsView(
       CaptureModeController::Get()->CreateRecordingOverlayView());
+  auto* overlay_window = overlay_widget_->GetNativeWindow();
+  overlay_window->SetEventTargeter(
+      std::make_unique<OverlayTargeter>(overlay_window));
   UpdateWidgetStacking();
 }
 
diff --git a/ash/constants/ash_features.cc b/ash/constants/ash_features.cc
index b5187451..1a122f84 100644
--- a/ash/constants/ash_features.cc
+++ b/ash/constants/ash_features.cc
@@ -607,6 +607,13 @@
 const base::Feature kFirmwareUpdaterApp = {"FirmwareUpdaterApp",
                                            base::FEATURE_DISABLED_BY_DEFAULT};
 
+// When enabled, there will be an alert bubble showing up when the device
+// returns from low brightness (e.g., sleep, closed cover) without a lock screen
+// and the active window is in fullscreen.
+// TODO(https://crbug.com/1107185): Remove this after the feature is launched.
+const base::Feature kFullscreenAlertBubble{"EnableFullscreenBubble",
+                                           base::FEATURE_DISABLED_BY_DEFAULT};
+
 // Enable ChromeOS FuseBox service.
 const base::Feature kFuseBox{"FuseBox", base::FEATURE_DISABLED_BY_DEFAULT};
 
@@ -1529,6 +1536,10 @@
   return base::FeatureList::IsEnabled(kFirmwareUpdaterApp);
 }
 
+bool IsFullscreenAlertBubbleEnabled() {
+  return base::FeatureList::IsEnabled(kFullscreenAlertBubble);
+}
+
 bool IsGaiaCloseViewMessageEnabled() {
   return base::FeatureList::IsEnabled(kGaiaCloseViewMessage);
 }
diff --git a/ash/constants/ash_features.h b/ash/constants/ash_features.h
index 5626b749..44e65e34 100644
--- a/ash/constants/ash_features.h
+++ b/ash/constants/ash_features.h
@@ -233,6 +233,8 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFilesTrash;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFiltersInRecents;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFirmwareUpdaterApp;
+COMPONENT_EXPORT(ASH_CONSTANTS)
+extern const base::Feature kFullscreenAlertBubble;
 COMPONENT_EXPORT(ASH_CONSTANTS) extern const base::Feature kFuseBox;
 COMPONENT_EXPORT(ASH_CONSTANTS)
 extern const base::Feature kGaiaCloseViewMessage;
@@ -551,6 +553,7 @@
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFastPairEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFileManagerSwaEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFirmwareUpdaterAppEnabled();
+COMPONENT_EXPORT(ASH_CONSTANTS) bool IsFullscreenAlertBubbleEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsGaiaCloseViewMessageEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsGaiaReauthEndpointEnabled();
 COMPONENT_EXPORT(ASH_CONSTANTS) bool IsHideArcMediaNotificationsEnabled();
diff --git a/ash/events/accessibility_event_rewriter.cc b/ash/events/accessibility_event_rewriter.cc
index d170b9f..d06e270 100644
--- a/ash/events/accessibility_event_rewriter.cc
+++ b/ash/events/accessibility_event_rewriter.cc
@@ -178,8 +178,16 @@
     return false;
 
   const ui::KeyEvent* key_event = event.AsKeyEvent();
+  ui::EventRewriterChromeOS::MutableKeyState state(key_event);
+  event_rewriter_chromeos_->RewriteModifierKeys(*key_event, &state);
+
+  std::unique_ptr<ui::Event> rewritten_event;
+  ui::EventRewriterChromeOS::BuildRewrittenKeyEvent(*key_event, state,
+                                                    &rewritten_event);
+  ui::KeyEvent* rewritten_key_event = rewritten_event.get()->AsKeyEvent();
+
   const auto& key =
-      switch_access_key_codes_to_capture_.find(key_event->key_code());
+      switch_access_key_codes_to_capture_.find(rewritten_key_event->key_code());
   if (key == switch_access_key_codes_to_capture_.end())
     return false;
 
@@ -214,7 +222,7 @@
       }
     } else {
       SwitchAccessCommand command =
-          key_code_to_switch_access_command_[key_event->key_code()];
+          key_code_to_switch_access_command_[rewritten_key_event->key_code()];
       delegate_->SendSwitchAccessCommand(command);
     }
   }
diff --git a/ash/events/accessibility_event_rewriter_unittest.cc b/ash/events/accessibility_event_rewriter_unittest.cc
index 28872d7..bd3361f 100644
--- a/ash/events/accessibility_event_rewriter_unittest.cc
+++ b/ash/events/accessibility_event_rewriter_unittest.cc
@@ -482,11 +482,13 @@
   std::vector<SwitchAccessCommand> commands_;
 };
 
-class SwitchAccessAccessibilityEventRewriterTest : public AshTestBase {
+class SwitchAccessAccessibilityEventRewriterTest
+    : public AshTestBase,
+      public ui::EventRewriterChromeOS::Delegate {
  public:
   SwitchAccessAccessibilityEventRewriterTest() {
     event_rewriter_chromeos_ = std::make_unique<ui::EventRewriterChromeOS>(
-        nullptr, nullptr, false, &fake_ime_keyboard_);
+        this, nullptr, false, &fake_ime_keyboard_);
   }
   ~SwitchAccessAccessibilityEventRewriterTest() override = default;
 
@@ -538,6 +540,11 @@
     rewriter->SetKeyCodesForSwitchAccessCommand(key_codes, command);
   }
 
+  void SetModifierRemapping(const std::string& pref_name,
+                            ui::chromeos::ModifierKey value) {
+    modifier_remapping_[pref_name] = static_cast<int>(value);
+  }
+
   const std::map<int, std::set<ui::InputDeviceType>> GetKeyCodesToCapture() {
     AccessibilityEventRewriter* rewriter =
         controller_->GetAccessibilityEventRewriterForTest();
@@ -554,6 +561,37 @@
     return std::map<int, SwitchAccessCommand>();
   }
 
+ private:
+  // ui::EventRewriterChromeOS::Delegate:
+  bool RewriteModifierKeys() override { return true; }
+
+  bool GetKeyboardRemappedPrefValue(const std::string& pref_name,
+                                    int* value) const override {
+    auto it = modifier_remapping_.find(pref_name);
+    if (it == modifier_remapping_.end())
+      return false;
+
+    *value = it->second;
+    return true;
+  }
+
+  bool TopRowKeysAreFunctionKeys() const override { return false; }
+
+  bool IsExtensionCommandRegistered(ui::KeyboardCode key_code,
+                                    int flags) const override {
+    return false;
+  }
+
+  bool IsSearchKeyAcceleratorReserved() const override { return false; }
+
+  bool NotifyDeprecatedRightClickRewrite() override { return false; }
+  bool NotifyDeprecatedFKeyRewrite() override { return false; }
+  bool NotifyDeprecatedSixPackKeyRewrite(ui::KeyboardCode key_code) override {
+    return false;
+  }
+
+  std::map<std::string, int> modifier_remapping_;
+
  protected:
   ui::test::EventGenerator* generator_ = nullptr;
   EventCapturer event_capturer_;
@@ -743,6 +781,41 @@
   EXPECT_EQ(command_map.end(), command_map.find(48));
 }
 
+TEST_F(SwitchAccessAccessibilityEventRewriterTest, RespectsModifierRemappings) {
+  // Set Control to be Switch Access' next button.
+  SetKeyCodesForSwitchAccessCommand(
+      {{ui::VKEY_CONTROL, {kSwitchAccessInternalDevice}}},
+      SwitchAccessCommand::kNext);
+
+  // Set Alt to be Switch Access' select button.
+  SetKeyCodesForSwitchAccessCommand(
+      {{ui::VKEY_MENU /* Alt key */, {kSwitchAccessInternalDevice}}},
+      SwitchAccessCommand::kSelect);
+
+  // Map Control key to Alt.
+  SetModifierRemapping(prefs::kLanguageRemapControlKeyTo,
+                       ui::chromeos::ModifierKey::kAltKey);
+
+  // Send a key event for Control.
+  generator_->PressKey(ui::VKEY_CONTROL, ui::EF_CONTROL_DOWN,
+                       1 /* keyboard id */);
+  // EventRewriterChromeOS actually omits the modifier flag on release.
+  generator_->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE, 1 /* keyboard id */);
+
+  // Verify Switch Access treated it like Alt.
+  EXPECT_EQ(1, delegate_->command_count());
+  EXPECT_EQ(SwitchAccessCommand::kSelect, delegate_->last_command());
+
+  // Send a key event for Alt.
+  generator_->PressKey(ui::VKEY_MENU, ui::EF_ALT_DOWN, 1 /* keyboard id */);
+  // EventRewriterChromeOS actually omits the modifier flag on release.
+  generator_->ReleaseKey(ui::VKEY_MENU, ui::EF_NONE, 1 /* keyboard id */);
+
+  // Verify Switch Access also treats that like Alt.
+  EXPECT_EQ(2, delegate_->command_count());
+  EXPECT_EQ(SwitchAccessCommand::kSelect, delegate_->last_command());
+}
+
 class MagnifierTestDelegate : public AccessibilityEventRewriterDelegate {
  public:
   MagnifierTestDelegate() = default;
diff --git a/ash/login/ui/login_auth_factors_view.cc b/ash/login/ui/login_auth_factors_view.cc
index 8e0e3c17..2b32068 100644
--- a/ash/login/ui/login_auth_factors_view.cc
+++ b/ash/login/ui/login_auth_factors_view.cc
@@ -8,6 +8,7 @@
 
 #include "ash/login/ui/arrow_button_view.h"
 #include "ash/login/ui/auth_icon_view.h"
+#include "ash/login/ui/lock_screen.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
@@ -313,10 +314,13 @@
     case PrioritizedAuthFactorViewState::kAuthenticated:
       // An auth factor has successfully authenticated. Show a green checkmark.
       ShowCheckmark();
-      // TODO(crbug.com/1233614): If we're on the login page, show "Signed in"
-      // instead of "Unlocked"
-      SetLabelTextAndAccessibleName(IDS_AUTH_FACTOR_LABEL_UNLOCKED,
-                                    IDS_AUTH_FACTOR_LABEL_UNLOCKED);
+      if (LockScreen::Get()->screen_type() == LockScreen::ScreenType::kLogin) {
+        SetLabelTextAndAccessibleName(IDS_AUTH_FACTOR_LABEL_SIGNED_IN,
+                                      IDS_AUTH_FACTOR_LABEL_SIGNED_IN);
+      } else {
+        SetLabelTextAndAccessibleName(IDS_AUTH_FACTOR_LABEL_UNLOCKED,
+                                      IDS_AUTH_FACTOR_LABEL_UNLOCKED);
+      }
       return;
     case PrioritizedAuthFactorViewState::kClickRequired:
       // An auth factor requires a click to enter. Show arrow button.
diff --git a/ash/login/ui/login_auth_factors_view_unittest.cc b/ash/login/ui/login_auth_factors_view_unittest.cc
index ea64b7b..0045e41 100644
--- a/ash/login/ui/login_auth_factors_view_unittest.cc
+++ b/ash/login/ui/login_auth_factors_view_unittest.cc
@@ -5,11 +5,13 @@
 #include "ash/login/ui/login_auth_factors_view.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/login/login_screen_controller.h"
 #include "ash/login/ui/arrow_button_view.h"
 #include "ash/login/ui/auth_factor_model.h"
 #include "ash/login/ui/auth_icon_view.h"
 #include "ash/login/ui/login_test_base.h"
 #include "ash/login/ui/login_test_utils.h"
+#include "ash/shell.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "base/callback_helpers.h"
 #include "base/feature_list.h"
@@ -336,7 +338,10 @@
   EXPECT_FALSE(test_api.arrow_nudge_animation()->GetVisible());
 }
 
-TEST_F(LoginAuthFactorsViewUnittest, Authenticated) {
+TEST_F(LoginAuthFactorsViewUnittest, Authenticated_LockScreen) {
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOCKED);
+  Shell::Get()->login_screen_controller()->ShowLockScreen();
   AddAuthFactors({AuthFactorType::kFingerprint, AuthFactorType::kSmartLock});
   LoginAuthFactorsView::TestApi test_api(view_);
   auth_factors_[0]->state_ = AuthFactorState::kAuthenticated;
@@ -353,6 +358,26 @@
             test_api.label()->GetText());
 }
 
+TEST_F(LoginAuthFactorsViewUnittest, Authenticated_LoginScreen) {
+  GetSessionControllerClient()->SetSessionState(
+      session_manager::SessionState::LOGIN_PRIMARY);
+  Shell::Get()->login_screen_controller()->ShowLoginScreen();
+  AddAuthFactors({AuthFactorType::kFingerprint, AuthFactorType::kSmartLock});
+  LoginAuthFactorsView::TestApi test_api(view_);
+  auth_factors_[0]->state_ = AuthFactorState::kAuthenticated;
+  auth_factors_[1]->state_ = AuthFactorState::kClickRequired;
+  test_api.UpdateState();
+
+  // Check that only the arrow button is shown and that the label has been
+  // updated.
+  EXPECT_TRUE(test_api.checkmark_icon()->GetVisible());
+  EXPECT_FALSE(test_api.arrow_button()->GetVisible());
+  EXPECT_FALSE(test_api.arrow_nudge_animation()->GetVisible());
+  EXPECT_FALSE(test_api.auth_factor_icon_row()->GetVisible());
+  EXPECT_EQ(l10n_util::GetStringUTF16(IDS_AUTH_FACTOR_LABEL_SIGNED_IN),
+            test_api.label()->GetText());
+}
+
 TEST_F(LoginAuthFactorsViewUnittest, ErrorTemporary) {
   AddAuthFactors({AuthFactorType::kFingerprint, AuthFactorType::kSmartLock});
   LoginAuthFactorsView::TestApi test_api(view_);
diff --git a/ash/projector/projector_annotation_tray.cc b/ash/projector/projector_annotation_tray.cc
index 258d1360..c3a92c5 100644
--- a/ash/projector/projector_annotation_tray.cc
+++ b/ash/projector/projector_annotation_tray.cc
@@ -114,21 +114,6 @@
 
 ProjectorAnnotationTray::~ProjectorAnnotationTray() = default;
 
-void ProjectorAnnotationTray::DeactivateActiveTool() {
-  // TODO(b/201664243): Use ProjectorControllerImpl to disable.
-  EnablePen(false);
-  EnableLaserPointer(false);
-  UpdateIcon();
-}
-
-void ProjectorAnnotationTray::UpdateIcon() {
-  ProjectorTool tool = GetCurrentTool();
-  image_view_->SetImage(gfx::CreateVectorIcon(
-      GetIconForTool(tool),
-      AshColorProvider::Get()->GetContentLayerColor(
-          AshColorProvider::ContentLayerType::kIconColorPrimary)));
-}
-
 bool ProjectorAnnotationTray::PerformAction(const ui::Event& event) {
   if (bubble_) {
     CloseBubble();
@@ -344,4 +329,19 @@
   UpdateIcon();
 }
 
+void ProjectorAnnotationTray::DeactivateActiveTool() {
+  // TODO(b/201664243): Use ProjectorControllerImpl to disable.
+  EnablePen(false);
+  EnableLaserPointer(false);
+  UpdateIcon();
+}
+
+void ProjectorAnnotationTray::UpdateIcon() {
+  ProjectorTool tool = GetCurrentTool();
+  image_view_->SetImage(gfx::CreateVectorIcon(
+      GetIconForTool(tool),
+      AshColorProvider::Get()->GetContentLayerColor(
+          AshColorProvider::ContentLayerType::kIconColorPrimary)));
+}
+
 }  // namespace ash
diff --git a/ash/projector/projector_annotation_tray.h b/ash/projector/projector_annotation_tray.h
index 16519125..f71c93ca 100644
--- a/ash/projector/projector_annotation_tray.h
+++ b/ash/projector/projector_annotation_tray.h
@@ -23,19 +23,6 @@
   ProjectorAnnotationTray& operator=(const ProjectorAnnotationTray&) = delete;
   ~ProjectorAnnotationTray() override;
 
- private:
-  // Deactives any annotation tool that is currently enabled and update the UI.
-  void DeactivateActiveTool();
-
-  // Updates the icon in the status area.
-  void UpdateIcon();
-
-  // TODO(b/193579885): Hook up to Pen tools.
-  void OnRedPressed() {}
-  void OnYellowPressed() {}
-  void OnBlackPressed() {}
-  void OnWhitePressed() {}
-
   // TrayBackgroundView:
   bool PerformAction(const ui::Event& event) override;
   void ClickedOutsideBubble() override;
@@ -51,6 +38,19 @@
   // ViewClickListener:
   void OnViewClicked(views::View* sender) override;
 
+ private:
+  // Deactives any annotation tool that is currently enabled and update the UI.
+  void DeactivateActiveTool();
+
+  // Updates the icon in the status area.
+  void UpdateIcon();
+
+  // TODO(b/193579885): Hook up to Pen tools.
+  void OnRedPressed() {}
+  void OnYellowPressed() {}
+  void OnBlackPressed() {}
+  void OnWhitePressed() {}
+
   // Image view of the tray icon.
   views::ImageView* const image_view_;
 
diff --git a/ash/public/cpp/session/session_controller.h b/ash/public/cpp/session/session_controller.h
index 962feff..fd5555d 100644
--- a/ash/public/cpp/session/session_controller.h
+++ b/ash/public/cpp/session/session_controller.h
@@ -47,6 +47,12 @@
   virtual void SetUserSessionOrder(
       const std::vector<uint32_t>& user_session_ids) = 0;
 
+  // Prepares ash for lock screen. Currently this ensures the current active
+  // window could not be used to mimic the lock screen. Lock screen is created
+  // after this call returns.
+  using PrepareForLockCallback = base::OnceClosure;
+  virtual void PrepareForLock(PrepareForLockCallback callback) = 0;
+
   // Runs the pre-lock animation to start locking ash. When the call returns,
   // |locked| == true means that the ash post-lock animation is finished and ash
   // is fully locked. Otherwise |locked| is false, which means something is
diff --git a/ash/public/cpp/wallpaper/online_wallpaper_variant.cc b/ash/public/cpp/wallpaper/online_wallpaper_variant.cc
index 1629a0d..f80296f 100644
--- a/ash/public/cpp/wallpaper/online_wallpaper_variant.cc
+++ b/ash/public/cpp/wallpaper/online_wallpaper_variant.cc
@@ -8,10 +8,10 @@
 
 OnlineWallpaperVariant::OnlineWallpaperVariant(
     uint64_t in_asset_id,
-    const GURL& in_url,
+    const GURL& in_raw_url,
     backdrop::Image::ImageType in_type)
-    : asset_id(in_asset_id), url(in_url), type(in_type) {
-  DCHECK(!url.is_empty());
+    : asset_id(in_asset_id), raw_url(in_raw_url), type(in_type) {
+  DCHECK(!raw_url.is_empty());
 }
 
 OnlineWallpaperVariant::OnlineWallpaperVariant(
@@ -25,7 +25,7 @@
 
 bool OnlineWallpaperVariant::operator==(
     const OnlineWallpaperVariant& other) const {
-  return (asset_id == other.asset_id) && (url == other.url) &&
+  return (asset_id == other.asset_id) && (raw_url == other.raw_url) &&
          (type == other.type);
 }
 
diff --git a/ash/public/cpp/wallpaper/online_wallpaper_variant.h b/ash/public/cpp/wallpaper/online_wallpaper_variant.h
index 7efaf56..e32476a 100644
--- a/ash/public/cpp/wallpaper/online_wallpaper_variant.h
+++ b/ash/public/cpp/wallpaper/online_wallpaper_variant.h
@@ -15,7 +15,7 @@
 
 struct ASH_PUBLIC_EXPORT OnlineWallpaperVariant {
   OnlineWallpaperVariant(uint64_t asset_id,
-                         const GURL& url,
+                         const GURL& raw_url,
                          backdrop::Image::ImageType type);
 
   OnlineWallpaperVariant(const OnlineWallpaperVariant& other);
@@ -33,7 +33,7 @@
   // The unique identifier of the wallpaper.
   uint64_t asset_id;
   // The wallpaper url.
-  GURL url;
+  GURL raw_url;
   // The image type.
   backdrop::Image::ImageType type;
 };
diff --git a/ash/session/fullscreen_controller.cc b/ash/session/fullscreen_controller.cc
index ef97ab4f..45c9017a 100644
--- a/ash/session/fullscreen_controller.cc
+++ b/ash/session/fullscreen_controller.cc
@@ -12,15 +12,12 @@
 #include "ash/session/session_controller_impl.h"
 #include "ash/shelf/shelf.h"
 #include "ash/shell.h"
-#include "ash/shell_delegate.h"
 #include "ash/wm/window_state.h"
 #include "ash/wm/wm_event.h"
 #include "chromeos/dbus/power_manager/backlight.pb.h"
 #include "chromeos/dbus/power_manager/idle.pb.h"
-#include "components/policy/core/browser/url_util.h"
 #include "components/prefs/pref_registry_simple.h"
 #include "components/prefs/pref_service.h"
-#include "components/url_matcher/url_matcher.h"
 
 namespace ash {
 
@@ -63,6 +60,9 @@
 }
 
 void FullscreenController::MaybeShowNotification() {
+  if (!features::IsFullscreenAlertBubbleEnabled())
+    return;
+
   auto* session_controller = Shell::Get()->session_controller();
 
   // Check if a user session is active to exclude OOBE process.
@@ -71,7 +71,12 @@
     return;
   }
 
-  // Check if the active window is fullscreen.
+  auto* prefs = session_controller->GetPrimaryUserPrefService();
+
+  if (!prefs->GetBoolean(prefs::kFullscreenAlertEnabled))
+    return;
+
+  // Check if the activate window is fullscreen.
   WindowState* active_window_state = WindowState::ForActiveWindow();
   if (!active_window_state || !active_window_state->IsFullscreen())
     return;
@@ -84,20 +89,6 @@
   if (shelf_visible && !active_window_state->GetHideShelfWhenFullscreen())
     return;
 
-  // Get the URL of the active window from the shell delegate.
-  const GURL& url =
-      Shell::Get()->shell_delegate()->GetLastCommittedURLForWindowIfAny(
-          active_window_state->window());
-
-  // Check if the URL is exempt from the notification by user pref.
-  auto* prefs = session_controller->GetPrimaryUserPrefService();
-  const base::ListValue* url_exempt_list = &base::Value::AsListValue(
-      *prefs->GetList(prefs::kFullscreenNotificationUrlExemptList));
-  url_matcher::URLMatcher url_matcher;
-  policy::url_util::AddAllowFilters(&url_matcher, url_exempt_list);
-  if (!url_matcher.MatchURL(url).empty())
-    return;
-
   if (!bubble_)
     bubble_ = std::make_unique<FullscreenNotificationBubble>();
 
diff --git a/ash/session/fullscreen_controller_unittest.cc b/ash/session/fullscreen_controller_unittest.cc
deleted file mode 100644
index 691de07..0000000
--- a/ash/session/fullscreen_controller_unittest.cc
+++ /dev/null
@@ -1,156 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "ash/session/fullscreen_controller.h"
-
-#include <memory>
-
-#include "ash/constants/ash_pref_names.h"
-#include "ash/session/fullscreen_notification_bubble.h"
-#include "ash/session/session_controller_impl.h"
-#include "ash/shell.h"
-#include "ash/test/ash_test_base.h"
-#include "ash/test_shell_delegate.h"
-#include "ash/wm/window_state.h"
-#include "testing/gtest/include/gtest/gtest.h"
-#include "ui/aura/client/aura_constants.h"
-#include "ui/aura/window.h"
-#include "ui/views/widget/widget.h"
-
-namespace ash {
-namespace {
-
-const GURL kActiveUrl = GURL("https://wwww.test.com");
-const GURL kEmptyUrl = GURL::EmptyGURL();
-
-constexpr char kNonMatchingPattern[] = "google.com";
-constexpr char kMatchingPattern[] = "test.com";
-constexpr char kWildcardPattern[] = "*";
-
-class FullscreenControllerTest : public AshTestBase {
- public:
-  FullscreenControllerTest() {}
-
-  FullscreenControllerTest(const FullscreenControllerTest&) = delete;
-  FullscreenControllerTest& operator=(const FullscreenControllerTest&) = delete;
-
-  ~FullscreenControllerTest() override {}
-
-  // AshTestBase:
-  void SetUp() override {
-    // Create a shell delegate and set the active URL.
-    auto test_shell_delegate = std::make_unique<TestShellDelegate>();
-    test_shell_delegate_ = test_shell_delegate.get();
-    test_shell_delegate->SetLastCommittedURLForWindow(kActiveUrl);
-
-    AshTestBase::SetUp(std::move(test_shell_delegate));
-
-    CreateFullscreenWindow();
-
-    fullscreen_controller_ =
-        Shell::Get()->session_controller()->fullscreen_controller_for_test();
-
-    GetSessionControllerClient()->LockScreen();
-  }
-
-  void TearDown() override {
-    window_.reset();
-    AshTestBase::TearDown();
-  }
-
-  void CreateFullscreenWindow() {
-    window_ = CreateTestWindow();
-    window_->SetProperty(aura::client::kShowStateKey,
-                         ui::SHOW_STATE_FULLSCREEN);
-  }
-
-  void SetFullscreenNotificationExemptList(const std::string& pattern) {
-    base::Value list(base::Value::Type::LIST);
-    list.Append(base::Value(pattern));
-    Shell::Get()->session_controller()->GetPrimaryUserPrefService()->Set(
-        prefs::kFullscreenNotificationUrlExemptList, list);
-  }
-
-  bool IsNotificationVisible() const {
-    return fullscreen_controller_->bubble_for_test() &&
-           fullscreen_controller_->bubble_for_test()->widget_for_test() &&
-           fullscreen_controller_->bubble_for_test()
-               ->widget_for_test()
-               ->IsVisible();
-  }
-
- protected:
-  std::unique_ptr<aura::Window> window_;
-
-  FullscreenController* fullscreen_controller_ = nullptr;
-
-  raw_ptr<TestShellDelegate> test_shell_delegate_ = nullptr;
-};
-
-// Test that the notification is not shown on session lock.
-TEST_F(FullscreenControllerTest, NotShowingOnLock) {
-  EXPECT_FALSE(IsNotificationVisible());
-}
-
-// Test that the notification is shown on session unlock if the exempt list pref
-// is unset.
-TEST_F(FullscreenControllerTest, UnsetPref_ShowingOnUnlock) {
-  GetSessionControllerClient()->UnlockScreen();
-  EXPECT_TRUE(IsNotificationVisible());
-}
-
-// Test that the notification is shown on session unlock if the URL of the
-// active window does not match any patterns from the exempt list.
-TEST_F(FullscreenControllerTest, NonMatchingPref_ShowingOnUnlock) {
-  SetFullscreenNotificationExemptList(kNonMatchingPattern);
-
-  GetSessionControllerClient()->UnlockScreen();
-  EXPECT_TRUE(IsNotificationVisible());
-}
-
-// Test that the notification is not shown on session unlock if the URL of the
-// active window matches a pattern from the exempt list.
-TEST_F(FullscreenControllerTest, MatchingPref_NotShowingOnUnlock) {
-  // Set up the URL exempt list with one matching and one non-matching pattern.
-  base::Value list(base::Value::Type::LIST);
-  list.Append(base::Value(kNonMatchingPattern));
-  list.Append(base::Value(kMatchingPattern));
-  Shell::Get()->session_controller()->GetPrimaryUserPrefService()->Set(
-      prefs::kFullscreenNotificationUrlExemptList, list);
-
-  GetSessionControllerClient()->UnlockScreen();
-  EXPECT_FALSE(IsNotificationVisible());
-}
-
-// Test that the notification is not shown on session unlock if the exempt list
-// includes the wildcard character.
-TEST_F(FullscreenControllerTest, WildcardPref_NotShowingOnUnlock) {
-  SetFullscreenNotificationExemptList(kWildcardPattern);
-
-  GetSessionControllerClient()->UnlockScreen();
-  EXPECT_FALSE(IsNotificationVisible());
-}
-
-// Test that the notification is shown on session unlock if the exempt list pref
-// is unset.
-TEST_F(FullscreenControllerTest, EmptyUrlUnsetPref_ShowingOnUnlock) {
-  test_shell_delegate_->SetLastCommittedURLForWindow(kEmptyUrl);
-
-  GetSessionControllerClient()->UnlockScreen();
-  EXPECT_TRUE(IsNotificationVisible());
-}
-
-// Test that the notification is not shown on session unlock if the exempt list
-// includes the wildcard character.
-TEST_F(FullscreenControllerTest, EmptyUrlWildcardPref_NotShowingOnUnlock) {
-  test_shell_delegate_->SetLastCommittedURLForWindow(kEmptyUrl);
-
-  SetFullscreenNotificationExemptList(kWildcardPattern);
-
-  GetSessionControllerClient()->UnlockScreen();
-  EXPECT_FALSE(IsNotificationVisible());
-}
-
-}  // namespace
-}  // namespace ash
diff --git a/ash/session/session_controller_impl.cc b/ash/session/session_controller_impl.cc
index 9b01fc0b..a6a6a2d 100644
--- a/ash/session/session_controller_impl.cc
+++ b/ash/session/session_controller_impl.cc
@@ -368,6 +368,11 @@
   }
 }
 
+void SessionControllerImpl::PrepareForLock(PrepareForLockCallback callback) {
+  FullscreenController::MaybeExitFullscreen();
+  std::move(callback).Run();
+}
+
 void SessionControllerImpl::StartLock(StartLockCallback callback) {
   DCHECK(start_lock_callback_.is_null());
   start_lock_callback_ = std::move(callback);
diff --git a/ash/session/session_controller_impl.h b/ash/session/session_controller_impl.h
index c4823ab2..7b9e5e8 100644
--- a/ash/session/session_controller_impl.h
+++ b/ash/session/session_controller_impl.h
@@ -192,6 +192,7 @@
   void UpdateUserSession(const UserSession& user_session) override;
   void SetUserSessionOrder(
       const std::vector<uint32_t>& user_session_order) override;
+  void PrepareForLock(PrepareForLockCallback callback) override;
   void StartLock(StartLockCallback callback) override;
   void NotifyChromeLockAnimationsComplete() override;
   void RunUnlockAnimation(RunUnlockAnimationCallback callback) override;
diff --git a/ash/session/session_controller_impl_unittest.cc b/ash/session/session_controller_impl_unittest.cc
index fd74374..d562520 100644
--- a/ash/session/session_controller_impl_unittest.cc
+++ b/ash/session/session_controller_impl_unittest.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/login_status.h"
 #include "ash/public/cpp/ash_prefs.h"
@@ -19,10 +20,12 @@
 #include "ash/shell.h"
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/test/ash_test_base.h"
+#include "ash/wm/window_state.h"
 #include "ash/wm/window_util.h"
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/run_loop.h"
+#include "base/test/scoped_feature_list.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "components/prefs/testing_pref_service.h"
 #include "components/user_manager/user_type.h"
@@ -152,56 +155,6 @@
   TestSessionObserver observer_;
 };
 
-class SessionControllerImplWithShellTest : public AshTestBase {
- public:
-  SessionControllerImplWithShellTest() = default;
-
-  SessionControllerImplWithShellTest(
-      const SessionControllerImplWithShellTest&) = delete;
-  SessionControllerImplWithShellTest& operator=(
-      const SessionControllerImplWithShellTest&) = delete;
-
-  ~SessionControllerImplWithShellTest() override = default;
-
-  // AshTestBase:
-  void SetUp() override {
-    AshTestBase::SetUp();
-    controller()->AddObserver(&observer_);
-
-    fullscreen_controller_ = controller()->fullscreen_controller_for_test();
-  }
-
-  void TearDown() override {
-    controller()->RemoveObserver(&observer_);
-    window_.reset();
-    AshTestBase::TearDown();
-  }
-
-  void CreateFullscreenWindow() {
-    window_ = CreateTestWindow();
-    window_->SetProperty(aura::client::kShowStateKey,
-                         ui::SHOW_STATE_FULLSCREEN);
-  }
-
-  bool IsNotificationVisible() const {
-    return fullscreen_controller_->bubble_for_test() &&
-           fullscreen_controller_->bubble_for_test()->widget_for_test() &&
-           fullscreen_controller_->bubble_for_test()
-               ->widget_for_test()
-               ->IsVisible();
-  }
-
-  SessionControllerImpl* controller() {
-    return Shell::Get()->session_controller();
-  }
-  const TestSessionObserver* observer() const { return &observer_; }
-
- private:
-  TestSessionObserver observer_;
-  std::unique_ptr<aura::Window> window_;
-  FullscreenController* fullscreen_controller_ = nullptr;
-};
-
 // Tests that the simple session info is reflected properly.
 TEST_F(SessionControllerImplTest, SimpleSessionInfo) {
   SessionInfo info;
@@ -280,7 +233,7 @@
 }
 
 // Tests that session state can be set and reflected properly.
-TEST_F(SessionControllerImplWithShellTest, SessionState) {
+TEST_F(SessionControllerImplTest, SessionState) {
   const struct {
     SessionState state;
     bool expected_is_screen_locked;
@@ -298,7 +251,7 @@
   FillDefaultSessionInfo(&info);
   for (const auto& test_case : kTestCases) {
     info.state = test_case.state;
-    controller()->SetSessionInfo(info);
+    SetSessionInfo(info);
 
     EXPECT_EQ(test_case.state, controller()->GetSessionState())
         << "Test case state=" << static_cast<int>(test_case.state);
@@ -429,21 +382,24 @@
 // Tests that user session is unblocked with a running unlock animation so that
 // focus rules can find a correct activatable window after screen lock is
 // dismissed.
-TEST_F(SessionControllerImplWithShellTest,
+TEST_F(SessionControllerImplTest,
        UserSessionUnblockedWithRunningUnlockAnimation) {
   SessionInfo info;
   FillDefaultSessionInfo(&info);
 
   // LOCKED means blocked user session.
   info.state = SessionState::LOCKED;
-  controller()->SetSessionInfo(info);
+  SetSessionInfo(info);
   EXPECT_TRUE(controller()->IsUserSessionBlocked());
 
+  // Mark a running unlock animation unblocks user session.
+  controller()->RunUnlockAnimation(base::OnceClosure());
+  EXPECT_FALSE(controller()->IsUserSessionBlocked());
+
   const struct {
     SessionState state;
-    bool expect_blocked_after_unlock_animation;
+    bool expected_is_user_session_blocked;
   } kTestCases[] = {
-      {SessionState::LOCKED, false},
       {SessionState::OOBE, true},
       {SessionState::LOGIN_PRIMARY, true},
       {SessionState::LOGGED_IN_NOT_ACTIVE, false},
@@ -452,13 +408,12 @@
   };
   for (const auto& test_case : kTestCases) {
     info.state = test_case.state;
-    controller()->SetSessionInfo(info);
+    SetSessionInfo(info);
 
     // Mark a running unlock animation.
-    base::RunLoop run_loop;
-    controller()->RunUnlockAnimation(run_loop.QuitClosure());
-    run_loop.Run();
-    EXPECT_EQ(test_case.expect_blocked_after_unlock_animation,
+    controller()->RunUnlockAnimation(base::OnceClosure());
+
+    EXPECT_EQ(test_case.expected_is_user_session_blocked,
               controller()->IsUserSessionBlocked())
         << "Test case state=" << static_cast<int>(test_case.state);
   }
@@ -850,26 +805,96 @@
   EXPECT_TRUE(widget->IsActive());
 }
 
+class SessionControllerImplLockStateChangedTest : public AshTestBase {
+ public:
+  SessionControllerImplLockStateChangedTest() {}
+
+  SessionControllerImplLockStateChangedTest(
+      const SessionControllerImplLockStateChangedTest&) = delete;
+  SessionControllerImplLockStateChangedTest& operator=(
+      const SessionControllerImplLockStateChangedTest&) = delete;
+
+  ~SessionControllerImplLockStateChangedTest() override {}
+
+  // AshTestBase:
+  void SetUp() override {
+    // Enable the FullscreenAlertBubble feature for testing.
+    feature_list_.InitAndEnableFeature(features::kFullscreenAlertBubble);
+
+    AshTestBase::SetUp();
+
+    // Set the FullscreenAlertEnabled pref value to true for testing.
+    Shell::Get()->session_controller()->GetPrimaryUserPrefService()->SetBoolean(
+        prefs::kFullscreenAlertEnabled, true);
+
+    fullscreen_controller_ =
+        Shell::Get()->session_controller()->fullscreen_controller_for_test();
+  }
+
+  void TearDown() override {
+    window_.reset();
+    AshTestBase::TearDown();
+  }
+
+  void CreateFullscreenWindow() {
+    window_ = CreateTestWindow();
+    window_->SetProperty(aura::client::kShowStateKey,
+                         ui::SHOW_STATE_FULLSCREEN);
+    window_state_ = WindowState::Get(window_.get());
+  }
+
+  void ExpectFullscreenNotificationShowing(bool expect_notification) {
+    if (expect_notification) {
+      views::Widget* fullscreen_notification_widget =
+          fullscreen_controller_->bubble_for_test()->widget_for_test();
+      EXPECT_TRUE(fullscreen_notification_widget->IsVisible());
+      return;
+    }
+    EXPECT_TRUE(!fullscreen_controller_->bubble_for_test() ||
+                !fullscreen_controller_->bubble_for_test()->widget_for_test() ||
+                !fullscreen_controller_->bubble_for_test()
+                     ->widget_for_test()
+                     ->IsVisible());
+  }
+
+ protected:
+  std::unique_ptr<aura::Window> window_;
+
+  WindowState* window_state_ = nullptr;
+  FullscreenController* fullscreen_controller_ = nullptr;
+
+  base::test::ScopedFeatureList feature_list_;
+};
+
+TEST_F(SessionControllerImplLockStateChangedTest, ExitFullscreenBeforeLock) {
+  CreateFullscreenWindow();
+  EXPECT_TRUE(window_state_->IsFullscreen());
+
+  base::RunLoop run_loop;
+  Shell::Get()->session_controller()->PrepareForLock(run_loop.QuitClosure());
+  EXPECT_FALSE(window_state_->IsFullscreen());
+}
+
 // Test that no full screen notification is shown on session lock or unlock when
 // there is no window in full screen mode.
-TEST_F(SessionControllerImplWithShellTest, WithoutFullscreenWindow) {
+TEST_F(SessionControllerImplLockStateChangedTest, WithoutFullscreenWindow) {
   GetSessionControllerClient()->LockScreen();
-  EXPECT_FALSE(IsNotificationVisible());
+  ExpectFullscreenNotificationShowing(false);
 
   GetSessionControllerClient()->UnlockScreen();
-  EXPECT_FALSE(IsNotificationVisible());
+  ExpectFullscreenNotificationShowing(false);
 }
 
 // Test that the full screen notification is shown on session unlock when there
 // is a window in full screen mode.
-TEST_F(SessionControllerImplWithShellTest, WithFullscreenWindow) {
+TEST_F(SessionControllerImplLockStateChangedTest, WithFullscreenWindow) {
   CreateFullscreenWindow();
 
   GetSessionControllerClient()->LockScreen();
-  EXPECT_FALSE(IsNotificationVisible());
+  ExpectFullscreenNotificationShowing(false);
 
   GetSessionControllerClient()->UnlockScreen();
-  EXPECT_TRUE(IsNotificationVisible());
+  ExpectFullscreenNotificationShowing(true);
 }
 
 }  // namespace
diff --git a/ash/shell_delegate.cc b/ash/shell_delegate.cc
index 48cd1a31..9d627cc 100644
--- a/ash/shell_delegate.cc
+++ b/ash/shell_delegate.cc
@@ -30,9 +30,4 @@
   return -1;
 }
 
-const GURL& ShellDelegate::GetLastCommittedURLForWindowIfAny(
-    aura::Window* window) {
-  return GURL::EmptyGURL();
-}
-
 }  // namespace ash
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index 50300954..df02db1 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -17,7 +17,6 @@
 #include "services/device/public/mojom/fingerprint.mojom-forward.h"
 #include "services/media_session/public/cpp/media_session_service.h"
 #include "ui/gfx/native_widget_types.h"
-#include "url/gurl.h"
 
 namespace aura {
 class Window;
@@ -130,10 +129,6 @@
   // persistent desks bar. Note, this will be removed once the feature is fully
   // launched or removed.
   virtual void OpenFeedbackPageForPersistentDesksBar() = 0;
-
-  // Returns the last committed URL from the web contents if the given |window|
-  // contains a browser frame, otherwise returns GURL::EmptyURL().
-  virtual const GURL& GetLastCommittedURLForWindowIfAny(aura::Window* window);
 };
 
 }  // namespace ash
diff --git a/ash/system/phonehub/camera_roll_opt_in_view.cc b/ash/system/phonehub/camera_roll_opt_in_view.cc
index 986e5041..b5733d0 100644
--- a/ash/system/phonehub/camera_roll_opt_in_view.cc
+++ b/ash/system/phonehub/camera_roll_opt_in_view.cc
@@ -10,6 +10,7 @@
 #include "ash/components/phonehub/camera_roll_manager.h"
 #include "ash/components/phonehub/util/histogram_util.h"
 #include "ash/public/cpp/new_window_delegate.h"
+#include "ash/system/phonehub/phone_hub_metrics.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
 #include "chromeos/components/multidevice/logging/logging.h"
 #include "ui/base/metadata/metadata_impl_macros.h"
@@ -26,12 +27,14 @@
 CameraRollOptInView::~CameraRollOptInView() = default;
 
 void CameraRollOptInView::SetUpButtonPressed() {
+  LogCameraRollOptInEvent(phone_hub_metrics::InterstitialScreenEvent::kConfirm);
   camera_roll_manager_->EnableCameraRollFeatureInSystemSetting();
   phonehub::util::LogCameraRollFeatureOptInEntryPoint(
       phonehub::util::CameraRollOptInEntryPoint::kOnboardingDialog);
 }
 
 void CameraRollOptInView::DismissButtonPressed() {
+  LogCameraRollOptInEvent(phone_hub_metrics::InterstitialScreenEvent::kDismiss);
   camera_roll_manager_->OnCameraRollOnboardingUiDismissed();
 }
 
diff --git a/ash/system/phonehub/camera_roll_view.cc b/ash/system/phonehub/camera_roll_view.cc
index 45688f8..18a469f 100644
--- a/ash/system/phonehub/camera_roll_view.cc
+++ b/ash/system/phonehub/camera_roll_view.cc
@@ -10,6 +10,7 @@
 #include "ash/style/ash_color_provider.h"
 #include "ash/system/phonehub/animated_loading_card.h"
 #include "ash/system/phonehub/camera_roll_thumbnail.h"
+#include "ash/system/phonehub/phone_hub_metrics.h"
 #include "ash/system/phonehub/phone_hub_view_ids.h"
 #include "ash/system/phonehub/ui_constants.h"
 #include "ash/system/tray/tray_constants.h"
@@ -226,6 +227,8 @@
       opt_in_view_->SetVisible(true);
       items_view_->SetVisible(false);
       SetVisible(true);
+      LogCameraRollOptInEvent(
+          phone_hub_metrics::InterstitialScreenEvent::kShown);
       break;
     case phonehub::CameraRollManager::CameraRollUiState::LOADING_VIEW:
       opt_in_view_->SetVisible(false);
diff --git a/ash/system/phonehub/phone_hub_metrics.cc b/ash/system/phonehub/phone_hub_metrics.cc
index d282bc0..4d44464 100644
--- a/ash/system/phonehub/phone_hub_metrics.cc
+++ b/ash/system/phonehub/phone_hub_metrics.cc
@@ -76,6 +76,10 @@
   base::UmaHistogramEnumeration("PhoneHub.NotificationOptInEvents", event);
 }
 
+void LogCameraRollOptInEvent(InterstitialScreenEvent event) {
+  base::UmaHistogramEnumeration("PhoneHub.CameraRoll.OptInEvents", event);
+}
+
 void LogTabContinuationChipClicked(int tab_index) {
   base::UmaHistogramCounts100("PhoneHub.TabContinuationChipClicked", tab_index);
 }
diff --git a/ash/system/phonehub/phone_hub_metrics.h b/ash/system/phonehub/phone_hub_metrics.h
index 53fcb16..446d59f 100644
--- a/ash/system/phonehub/phone_hub_metrics.h
+++ b/ash/system/phonehub/phone_hub_metrics.h
@@ -102,6 +102,9 @@
 // Logs an |event| for the notification opt-in prompt.
 void LogNotificationOptInEvent(InterstitialScreenEvent event);
 
+// Logs an |event| for the camera roll opt-in dialog in phone hub view.
+void LogCameraRollOptInEvent(InterstitialScreenEvent event);
+
 // Logs the |tab_index| of the tab continuation chip that was clicked.
 void LogTabContinuationChipClicked(int tab_index);
 
diff --git a/ash/test_shell_delegate.cc b/ash/test_shell_delegate.cc
index 6f9b5c54..3a5a2ed 100644
--- a/ash/test_shell_delegate.cc
+++ b/ash/test_shell_delegate.cc
@@ -13,7 +13,6 @@
 #include "ash/system/tray/system_tray_notifier.h"
 #include "ash/wm/gestures/back_gesture/test_back_gesture_contextual_nudge_delegate.h"
 #include "ui/gfx/image/image.h"
-#include "url/gurl.h"
 
 namespace ash {
 
@@ -99,13 +98,4 @@
   return base::FilePath();
 }
 
-const GURL& TestShellDelegate::GetLastCommittedURLForWindowIfAny(
-    aura::Window* window) {
-  return last_committed_url_;
-}
-
-void TestShellDelegate::SetLastCommittedURLForWindow(const GURL& url) {
-  last_committed_url_ = url;
-}
-
 }  // namespace ash
diff --git a/ash/test_shell_delegate.h b/ash/test_shell_delegate.h
index 81713df..cc2c940 100644
--- a/ash/test_shell_delegate.h
+++ b/ash/test_shell_delegate.h
@@ -11,7 +11,6 @@
 #include "base/callback.h"
 #include "chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
-#include "url/gurl.h"
 
 namespace ash {
 
@@ -55,7 +54,6 @@
       override;
   bool IsSessionRestoreInProgress() const override;
   void SetUpEnvironmentForLockedFullscreen(bool locked) override {}
-  const GURL& GetLastCommittedURLForWindowIfAny(aura::Window* window) override;
 
   void SetCanGoBack(bool can_go_back);
   void SetShouldWaitForTouchAck(bool should_wait_for_touch_ack);
@@ -63,7 +61,6 @@
   bool IsLoggingRedirectDisabled() const override;
   base::FilePath GetPrimaryUserDownloadsFolder() const override;
   void OpenFeedbackPageForPersistentDesksBar() override {}
-  void SetLastCommittedURLForWindow(const GURL& url);
 
  private:
   // True if the current top window can go back.
@@ -82,8 +79,6 @@
   bool session_restore_in_progress_ = false;
 
   MultiDeviceSetupBinder multidevice_setup_binder_;
-
-  GURL last_committed_url_ = GURL::EmptyGURL();
 };
 
 }  // namespace ash
diff --git a/ash/wallpaper/wallpaper_controller_impl.cc b/ash/wallpaper/wallpaper_controller_impl.cc
index 3c29228..f7932bfe6 100644
--- a/ash/wallpaper/wallpaper_controller_impl.cc
+++ b/ash/wallpaper/wallpaper_controller_impl.cc
@@ -425,7 +425,7 @@
       GetAppropriateResolution();
 
   for (const auto& variant : variants) {
-    const std::string& url = variant.url.spec();
+    const std::string& url = variant.raw_url.spec();
     base::FilePath variant_path = GetOnlineWallpaperPath(url, resolution);
     base::FilePath large_variant_path = GetOnlineWallpaperPath(
         url, WallpaperControllerImpl::WALLPAPER_RESOLUTION_LARGE);
@@ -632,7 +632,7 @@
         base::NumberToString(variant.asset_id));
     online_wallpaper_variant_dict.SetStringPath(
         WallpaperControllerImpl::kOnlineWallpaperUrlNodeName,
-        variant.url.spec());
+        variant.raw_url.spec());
     online_wallpaper_variant_dict.SetIntPath(
         WallpaperControllerImpl::kOnlineWallpaperTypeNodeName,
         static_cast<int>(variant.type));
@@ -2624,7 +2624,7 @@
 
     for (size_t i = 0; i < variants.size(); i++) {
       ImageDownloader::Get()->Download(
-          GURL(variants.at(i).url.spec() + GetBackdropWallpaperSuffix()),
+          GURL(variants.at(i).raw_url.spec() + GetBackdropWallpaperSuffix()),
           NO_TRAFFIC_ANNOTATION_YET,
           base::BindOnce(
               &WallpaperControllerImpl::OnOnlineWallpaperVariantDownloaded,
@@ -2647,14 +2647,14 @@
   const std::vector<OnlineWallpaperVariant>& variants = params.variants;
   const OnlineWallpaperVariant& current_variant = variants.at(current_index);
   // Keep track of each downloaded image.
-  url_to_image_map_.insert({current_variant.url.spec(), image});
+  url_to_image_map_.insert({current_variant.raw_url.spec(), image});
 
   // Save the image to disk.
   image.EnsureRepsForSupportedScales();
   gfx::ImageSkia deep_copy(image.DeepCopy());
   sequenced_task_runner_->PostTask(
       FROM_HERE,
-      base::BindOnce(&SaveOnlineWallpaper, current_variant.url.spec(),
+      base::BindOnce(&SaveOnlineWallpaper, current_variant.raw_url.spec(),
                      params.layout, deep_copy));
   std::move(on_done).Run();
 }
@@ -2949,7 +2949,7 @@
     if (iter != info.variants.end()) {
       SetOnlineWallpaper(
           ash::OnlineWallpaperParams{account_id, iter->asset_id,
-                                     GURL(iter->url), info.collection_id,
+                                     GURL(iter->raw_url), info.collection_id,
                                      info.layout, /*preview_mode=*/false,
                                      /*from_user=*/false, daily_refresh_enabled,
                                      info.unit_id, info.variants},
@@ -3008,7 +3008,7 @@
   } else {
     SetOnlineWallpaper(
         ash::OnlineWallpaperParams{
-            params.account_id, iter->asset_id, GURL(iter->url),
+            params.account_id, iter->asset_id, GURL(iter->raw_url),
             params.collection_id, params.layout, params.preview_mode,
             params.from_user, params.daily_refresh_enabled, unit_id, variants},
         std::move(callback));
diff --git a/ash/wallpaper/wallpaper_controller_unittest.cc b/ash/wallpaper/wallpaper_controller_unittest.cc
index 5ab6cb5d..021773af 100644
--- a/ash/wallpaper/wallpaper_controller_unittest.cc
+++ b/ash/wallpaper/wallpaper_controller_unittest.cc
@@ -280,7 +280,7 @@
         base::NumberToString(variant.asset_id));
     online_wallpaper_variant_dict.SetStringPath(
         WallpaperControllerImpl::kOnlineWallpaperUrlNodeName,
-        variant.url.spec());
+        variant.raw_url.spec());
     online_wallpaper_variant_dict.SetIntPath(
         WallpaperControllerImpl::kOnlineWallpaperTypeNodeName,
         static_cast<int>(variant.type));
diff --git a/ash/webui/personalization_app/resources/BUILD.gn b/ash/webui/personalization_app/resources/BUILD.gn
index 6af45b90..a175a2b 100644
--- a/ash/webui/personalization_app/resources/BUILD.gn
+++ b/ash/webui/personalization_app/resources/BUILD.gn
@@ -48,11 +48,11 @@
   "trusted/personalization_router_element.ts",
   "trusted/personalization_toast_element.ts",
   "trusted/personalization_breadcrumb_element.ts",
-  "trusted/wallpaper/google_photos_albums_element.js",
-  "trusted/wallpaper/google_photos_collection_element.js",
+  "trusted/wallpaper/google_photos_albums_element.ts",
+  "trusted/wallpaper/google_photos_collection_element.ts",
   "trusted/wallpaper/google_photos_photos_by_album_id_element.ts",
   "trusted/wallpaper/google_photos_photos_element.js",
-  "trusted/wallpaper/google_photos_zero_state_element.js",
+  "trusted/wallpaper/google_photos_zero_state_element.ts",
   "trusted/wallpaper/local_images_element.ts",
   "trusted/wallpaper/styles.ts",
   "trusted/wallpaper/wallpaper_collections_element.ts",
diff --git a/ash/webui/personalization_app/resources/common/constants.ts b/ash/webui/personalization_app/resources/common/constants.ts
index 3545de55..c38c158 100644
--- a/ash/webui/personalization_app/resources/common/constants.ts
+++ b/ash/webui/personalization_app/resources/common/constants.ts
@@ -17,6 +17,7 @@
 
 export const trustedOrigin = 'chrome://personalization';
 
+export const kMaximumGooglePhotosPreviews = 4;
 export const kMaximumLocalImagePreviews = 3;
 
 export enum EventType {
diff --git a/ash/webui/personalization_app/resources/trusted/iframe_api.ts b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
index b081e540..c8decc6 100644
--- a/ash/webui/personalization_app/resources/trusted/iframe_api.ts
+++ b/ash/webui/personalization_app/resources/trusted/iframe_api.ts
@@ -10,6 +10,7 @@
 
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 import * as constants from '../common/constants.js';
 import {isNonEmptyArray} from '../common/utils.js';
@@ -44,7 +45,7 @@
  * Sends the list of Google Photos photos to untrusted.
  */
 export function sendGooglePhotosPhotos(
-    target: Window, photos: Array<any>|null) {
+    target: Window, photos: Array<Url>|null) {
   const event: constants.SendGooglePhotosPhotosEvent = {
     type: constants.EventType.SEND_GOOGLE_PHOTOS_PHOTOS,
     photos
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.js b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.ts
similarity index 63%
rename from ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.js
rename to ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.ts
index ab0b20d..6971dea 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.js
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_albums_element.ts
@@ -8,16 +8,21 @@
 
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import './styles.js';
-import '/common/styles.js';
+import '../../common/styles.js';
 
-import {isSelectionEvent} from '/common/utils.js';
 import {assert} from 'chrome://resources/js/assert.m.js';
+import {IronListElement} from 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
 import {afterNextRender, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
+import {isSelectionEvent} from '../../common/utils.js';
+import {WallpaperCollection} from '../personalization_app.mojom-webui.js';
 import {PersonalizationRouter} from '../personalization_router_element.js';
 import {WithPersonalizationStore} from '../personalization_store.js';
 
-/** @polymer */
+export interface GooglePhotosAlbums {
+  $: {grid: IronListElement;};
+}
+
 export class GooglePhotosAlbums extends WithPersonalizationStore {
   static get is() {
     return 'google-photos-albums';
@@ -29,10 +34,6 @@
 
   static get properties() {
     return {
-      /**
-       * Whether or not this element is currently hidden.
-       * @type {boolean}
-       */
       hidden: {
         type: Boolean,
         value: true,
@@ -40,54 +41,41 @@
         observer: 'onHiddenChanged_',
       },
 
-      /**
-       * The list of albums.
-       * @type {?Array<WallpaperCollection>}
-       * @private
-       */
-      albums_: {
-        type: Array,
-      },
-
-      /**
-       * Whether the list of albums is currently loading.
-       * @type {boolean}
-       * @private
-       */
-      albumsLoading_: {
-        type: Boolean,
-      },
+      albums_: Array,
+      albumsLoading_: Boolean,
     };
   }
 
-  /** @override */
+  /** Whether or not this element is currently hidden. */
+  hidden: boolean;
+
+  /** The list of albums. */
+  private albums_: WallpaperCollection[]|null|undefined;
+
+  /** Whether the list of albums is currently loading. */
+  private albumsLoading_: boolean;
+
   connectedCallback() {
     super.connectedCallback();
 
-    this.watch('albums_', state => state.wallpaper.googlePhotos.albums);
-    this.watch(
+    this.watch<GooglePhotosAlbums['albums_']>(
+        'albums_', state => state.wallpaper.googlePhotos.albums);
+    this.watch<GooglePhotosAlbums['albumsLoading_']>(
         'albumsLoading_', state => state.wallpaper.loading.googlePhotos.albums);
 
     this.updateFromStore();
   }
 
-  /**
-   * Invoked on selection of an album.
-   * @param {!Event} e
-   * @private
-   */
-  onAlbumSelected_(e) {
+  /** Invoked on selection of an album. */
+  private onAlbumSelected_(e: Event&{model: {album: WallpaperCollection}}) {
     assert(e.model.album);
     if (isSelectionEvent(e)) {
       PersonalizationRouter.instance().selectGooglePhotosAlbum(e.model.album);
     }
   }
 
-  /**
-   * Invoked on changes to this element's |hidden| state.
-   * @private
-   */
-  onHiddenChanged_() {
+  /** Invoked on changes to this element's |hidden| state. */
+  private onHiddenChanged_() {
     if (this.hidden) {
       return;
     }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.js b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.js
deleted file mode 100644
index dc15978..0000000
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.js
+++ /dev/null
@@ -1,226 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Polymer element that fetches and displays the Google Photos
- * collection.
- */
-
-import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
-import './styles.js';
-import '/common/styles.js';
-
-import {isNonEmptyArray} from '/common/utils.js';
-import {assertNotReached} from 'chrome://resources/js/assert.m.js';
-import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-
-import {WithPersonalizationStore} from '../personalization_store.js';
-
-import {initializeGooglePhotosData} from './wallpaper_controller.js';
-import {getWallpaperProvider} from './wallpaper_interface_provider.js';
-
-/**
- * Enumeration of supported tabs.
- * @enum {string}
- */
-const Tab = {
-  Albums: 'albums',
-  Photos: 'photos',
-  PhotosByAlbumId: 'photosByAlbumId',
-};
-
-/** @polymer */
-export class GooglePhotosCollection extends WithPersonalizationStore {
-  static get is() {
-    return 'google-photos-collection';
-  }
-
-  static get template() {
-    return html`{__html_template__}`;
-  }
-
-  static get properties() {
-    return {
-      /**
-       * The currently selected album id.
-       * @type {?string}
-       */
-      albumId: {
-        type: String,
-        observer: 'onAlbumIdChanged_',
-      },
-
-      /**
-       * Whether or not this element is currently hidden.
-       * @type {boolean}
-       */
-      hidden: {
-        type: Boolean,
-        value: true,
-        reflectToAttribute: true,
-        observer: 'onHiddenChanged_',
-      },
-
-      /**
-       * The list of albums.
-       * @type {?Array<WallpaperCollection>}
-       * @private
-       */
-      albums_: {
-        type: Array,
-      },
-
-      /**
-       * The list of photos.
-       * @type {?Array<undefined>}
-       * @private
-       */
-      photos_: {
-        type: Array,
-      },
-
-      /**
-       * The currently selected tab.
-       * @type {!Tab}
-       * @private
-       */
-      tab_: {
-        type: String,
-        value: Tab.Photos,
-      },
-    };
-  }
-
-  /** @override */
-  constructor() {
-    super();
-    /** @const @private */
-    this.wallpaperProvider_ = getWallpaperProvider();
-  }
-
-  /** @override */
-  connectedCallback() {
-    super.connectedCallback();
-
-    this.watch('albums_', state => state.wallpaper.googlePhotos.albums);
-    this.watch('photos_', state => state.wallpaper.googlePhotos.photos);
-    this.updateFromStore();
-
-    initializeGooglePhotosData(this.wallpaperProvider_, this.getStore());
-  }
-
-  /**
-   * Invoked on changes to the currently selected |albumId|.
-   * @private
-   */
-  onAlbumIdChanged_() {
-    this.tab_ = this.albumId ? Tab.PhotosByAlbumId : Tab.Albums;
-  }
-
-  /**
-   * Invoked on changes to this element's |hidden| state.
-   * @private
-   */
-  onHiddenChanged_() {
-    if (this.hidden) {
-      return;
-    }
-
-    document.title = this.i18n('googlePhotosLabel');
-    this.shadowRoot.getElementById('main').focus();
-  }
-
-  /**
-   * Invoked on tab selected.
-   * @param {!Event} e
-   * @private
-   */
-  onTabSelected_(e) {
-    switch (e.currentTarget.id) {
-      case 'albumsTab':
-        this.tab_ = Tab.Albums;
-        return;
-      case 'photosTab':
-        this.tab_ = Tab.Photos;
-        return;
-      default:
-        assertNotReached();
-        return;
-    }
-  }
-
-  /**
-   * Whether the list of albums is empty.
-   * @return {boolean}
-   * @private
-   */
-  isAlbumsEmpty_() {
-    return !isNonEmptyArray(this.albums_);
-  }
-
-  /**
-   * Whether the albums tab is currently selected.
-   * @return {boolean}
-   * @private
-   */
-  isAlbumsTabSelected_() {
-    return this.tab_ === Tab.Albums;
-  }
-
-  /**
-   * Whether the albums tab is currently visible.
-   * @return {boolean}
-   * @private
-   */
-  isAlbumsTabVisible_() {
-    return this.isAlbumsTabSelected_() && !this.hidden;
-  }
-
-  /**
-   * Whether the photos by album id tab is currently visible.
-   * @return {boolean}
-   * @private
-   */
-  isPhotosByAlbumIdTabVisible_() {
-    return this.tab_ === Tab.PhotosByAlbumId && !this.hidden;
-  }
-
-  /**
-   * Whether the list of photos is empty.
-   * @return {boolean}
-   * @private
-   */
-  isPhotosEmpty_() {
-    return !isNonEmptyArray(this.photos_);
-  }
-
-  /**
-   * Whether the photos tab is currently selected.
-   * @return {boolean}
-   * @private
-   */
-  isPhotosTabSelected_() {
-    return this.tab_ === Tab.Photos;
-  }
-
-  /**
-   * Whether the photos tab is currently visible.
-   * @return {boolean}
-   * @private
-   */
-  isPhotosTabVisible_() {
-    return this.isPhotosTabSelected_() && !this.hidden;
-  }
-
-  /**
-   * Whether the tab strip is currently visible.
-   * @return {boolean}
-   * @private
-   */
-  isTabStripVisible_() {
-    return !this.albumId && !this.isAlbumsEmpty_();
-  }
-}
-
-customElements.define(GooglePhotosCollection.is, GooglePhotosCollection);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
new file mode 100644
index 0000000..d064f078
--- /dev/null
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_collection_element.ts
@@ -0,0 +1,173 @@
+// 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 Polymer element that fetches and displays the Google Photos
+ * collection.
+ */
+
+import 'chrome://resources/cr_elements/cr_button/cr_button.m.js';
+import './styles.js';
+import '/common/styles.js';
+
+import {assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
+import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {isNonEmptyArray} from '../../common/utils.js';
+import {WallpaperCollection, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
+import {WithPersonalizationStore} from '../personalization_store.js';
+
+import {initializeGooglePhotosData} from './wallpaper_controller.js';
+import {getWallpaperProvider} from './wallpaper_interface_provider.js';
+
+/** Enumeration of supported tabs. */
+enum Tab {
+  Albums,
+  Photos,
+  PhotosByAlbumId,
+}
+
+export interface GooglePhotosCollection {
+  $: {main: HTMLElement;};
+}
+
+export class GooglePhotosCollection extends WithPersonalizationStore {
+  static get is() {
+    return 'google-photos-collection';
+  }
+
+  static get template() {
+    return html`{__html_template__}`;
+  }
+
+  static get properties() {
+    return {
+      albumId: {
+        type: String,
+        observer: 'onAlbumIdChanged_',
+      },
+
+      hidden: {
+        type: Boolean,
+        value: true,
+        reflectToAttribute: true,
+        observer: 'onHiddenChanged_',
+      },
+
+      albums_: Array,
+      photos_: Array,
+
+      tab_: {
+        type: String,
+        value: Tab.Photos,
+      },
+    };
+  }
+
+  /** The currently selected album id. */
+  albumId: string|undefined;
+
+  /** Whether or not this element is currently hidden. */
+  hidden: boolean;
+
+  /** The list of albums. */
+  private albums_: WallpaperCollection[]|null|undefined;
+
+  /** The list of photos. */
+  private photos_: Url[]|null|undefined;
+
+  /** The currently selected tab. */
+  private tab_: Tab;
+
+  /** The singleton wallpaper provider interface. */
+  private wallpaperProvider_: WallpaperProviderInterface =
+      getWallpaperProvider();
+
+  connectedCallback() {
+    super.connectedCallback();
+
+    this.watch<GooglePhotosCollection['albums_']>(
+        'albums_', state => state.wallpaper.googlePhotos.albums);
+    this.watch<GooglePhotosCollection['photos_']>(
+        'photos_', state => state.wallpaper.googlePhotos.photos);
+
+    this.updateFromStore();
+
+    initializeGooglePhotosData(this.wallpaperProvider_, this.getStore());
+  }
+
+  /** Invoked on changes to the currently selected |albumId|. */
+  private onAlbumIdChanged_() {
+    this.tab_ = this.albumId ? Tab.PhotosByAlbumId : Tab.Albums;
+  }
+
+  /** Invoked on changes to this element's |hidden| state. */
+  private onHiddenChanged_() {
+    if (this.hidden) {
+      return;
+    }
+
+    document.title = this.i18n('googlePhotosLabel');
+    this.$.main.focus();
+  }
+
+  /** Invoked on tab selected. */
+  onTabSelected_(e: Event) {
+    const currentTarget: HTMLElement = e.currentTarget as HTMLElement;
+    switch (currentTarget.id) {
+      case 'albumsTab':
+        this.tab_ = Tab.Albums;
+        return;
+      case 'photosTab':
+        this.tab_ = Tab.Photos;
+        return;
+      default:
+        assertNotReached();
+        return;
+    }
+  }
+
+  /** Whether the list of albums is empty. */
+  private isAlbumsEmpty_(): boolean {
+    return !isNonEmptyArray(this.albums_);
+  }
+
+  /** Whether the albums tab is currently selected. */
+  private isAlbumsTabSelected_(): boolean {
+    return this.tab_ === Tab.Albums;
+  }
+
+  /** Whether the albums tab is currently visible. */
+  private isAlbumsTabVisible_(): boolean {
+    return this.isAlbumsTabSelected_() && !this.hidden;
+  }
+
+  /** Whether the photos by album id tab is currently visible. */
+  private isPhotosByAlbumIdTabVisible_(): boolean {
+    return this.tab_ === Tab.PhotosByAlbumId && !this.hidden;
+  }
+
+  /** Whether the list of photos is empty. */
+  private isPhotosEmpty_(): boolean {
+    return !isNonEmptyArray(this.photos_);
+  }
+
+  /** Whether the photos tab is currently selected. */
+  private isPhotosTabSelected_(): boolean {
+    return this.tab_ === Tab.Photos;
+  }
+
+  /** Whether the photos tab is currently visible. */
+  private isPhotosTabVisible_(): boolean {
+    return this.isPhotosTabSelected_() && !this.hidden;
+  }
+
+  /** Whether the tab strip is currently visible. */
+  private isTabStripVisible_(): boolean {
+    return !this.albumId && !this.isAlbumsEmpty_();
+  }
+}
+
+customElements.define(GooglePhotosCollection.is, GooglePhotosCollection);
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
index 50cf61e..d8842317 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_by_album_id_element.ts
@@ -77,18 +77,13 @@
             'computeAlbum_(albumId, photosByAlbumId_, photosByAlbumIdLoading_)',
       },
 
-      photosByAlbumId_: {
-        type: Object,
-      },
-
-      photosByAlbumIdLoading_: {
-        type: Object,
-      },
+      photosByAlbumId_: Object,
+      photosByAlbumIdLoading_: Object,
     };
   }
 
   /** The currently selected album id. */
-  albumId: string;
+  albumId: string|undefined;
 
   /** Whether or not this element is currently hidden. */
   hidden: boolean;
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html
index 4ef7d35..ecebee8 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.html
@@ -26,6 +26,13 @@
     display: flex;
     justify-content: center;
   }
+
+  .photo img {
+    flex: 1 1 auto;
+    height: 100%;
+    object-fit: cover;
+    width: 100%;
+  }
 </style>
 <iron-list id="grid" items="[[photosByRow_]]" as="row">
   <template>
@@ -34,7 +41,7 @@
       <template is="dom-repeat" items="[[row]]" as="photo">
         <wallpaper-grid-item>
           <div class="photo" colindex$="[[index]]" tabindex="-1">
-            [[photo]]
+            <img is="cr-auto-img" auto-src="[[photo.url]]"></img>
           </div>
         </wallpaper-grid-item>
       </template>
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.js b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.js
index 6f82a64..612ea098 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.js
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_photos_element.js
@@ -7,11 +7,13 @@
  */
 
 import 'chrome://resources/polymer/v3_0/iron-list/iron-list.js';
+import 'chrome://resources/cr_elements/cr_auto_img/cr_auto_img.js';
 import './styles.js';
 import '/common/styles.js';
 
 import {getNumberOfGridItemsPerRow, isNonEmptyArray, isSelectionEvent, normalizeKeyForRTL} from '/common/utils.js';
 import {assert, assertNotReached} from 'chrome://resources/js/assert.m.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {afterNextRender, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {WithPersonalizationStore} from '../personalization_store.js';
@@ -51,7 +53,7 @@
 
       /**
        * The list of photos.
-       * @type {?Array<undefined>}
+       * @type {?Array<Url>}
        * @private
        */
       photos_: {
@@ -61,7 +63,7 @@
       /**
        * The list of |photos_| split into the appropriate number of
        * |photosPerRow_| so as to be rendered in a grid.
-       * @type {?Array<Array<undefined>>}
+       * @type {?Array<Array<Url>>}
        * @private
        */
       photosByRow_: {
@@ -187,7 +189,7 @@
 
   /**
    * Invoked to compute |photosByRow_|.
-   * @return {?Array<Array<undefined>>}
+   * @return {?Array<Array<Url>>}
    * @private
    */
   computePhotosByRow_() {
@@ -197,13 +199,11 @@
     if (!isNonEmptyArray(this.photos_)) {
       return null;
     }
-    let index = 0;
     return Array.from(
         {length: Math.ceil(this.photos_.length / this.photosPerRow_)},
         (_, i) => {
           i *= this.photosPerRow_;
-          const row = this.photos_.slice(i, i + this.photosPerRow_)
-                          .map(photo => index++);
+          const row = this.photos_.slice(i, i + this.photosPerRow_);
           while (row.length < this.photosPerRow_) {
             row.push(undefined);
           }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.js b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.ts
similarity index 85%
rename from ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.js
rename to ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.ts
index 9ed94244..6825340e 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.js
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/google_photos_zero_state_element.ts
@@ -7,13 +7,12 @@
  */
 
 import './styles.js';
-import '/common/styles.js';
+import '../../common/styles.js';
 
 import {html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {WithPersonalizationStore} from '../personalization_store.js';
 
-/** @polymer */
 export class GooglePhotosZeroState extends WithPersonalizationStore {
   static get is() {
     return 'google-photos-zero-state';
@@ -23,12 +22,8 @@
     return html`{__html_template__}`;
   }
 
-  /**
-   * Returns the message to be displayed.
-   * @return {string}
-   * @private
-   */
-  getMessage_() {
+  /** Returns the message to be displayed. */
+  private getMessage_(): string {
     return this.i18nAdvanced('googlePhotosZeroStateMessage', {
       substitutions: [
         '<a target="_blank" href="https://photos.google.com">photos.google.com</a>'
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
index f251aa95..bcf0db3 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_actions.ts
@@ -5,6 +5,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {Action} from 'chrome://resources/js/cr/ui/store.js';
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {CurrentWallpaper, WallpaperCollection, WallpaperImage} from '../personalization_app.mojom-webui.js';
 import {DisplayableImage} from '../personalization_reducers.js';
@@ -270,13 +271,11 @@
 
 export type SetGooglePhotosPhotosAction = Action&{
   name: WallpaperActionName.SET_GOOGLE_PHOTOS_PHOTOS;
-  photos: unknown[]|null;
+  photos: Url[]|null;
 };
 
-/**
- * Sets the list of Google Photos photos. May be called with null on error.
- */
-export function setGooglePhotosPhotosAction(photos: unknown[]|
+/** Sets the list of Google Photos photos. May be called with null on error. */
+export function setGooglePhotosPhotosAction(photos: Url[]|
                                             null): SetGooglePhotosPhotosAction {
   return {photos, name: WallpaperActionName.SET_GOOGLE_PHOTOS_PHOTOS};
 }
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
index 1bab1e67..af08807 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_collections_element.ts
@@ -11,9 +11,10 @@
 import './styles.js';
 
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 import {afterNextRender, html} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {kMaximumLocalImagePreviews} from '../../common/constants.js';
+import {kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from '../../common/constants.js';
 import {isNonEmptyArray, isNullOrArray, isNullOrNumber, promisifyOnload} from '../../common/utils.js';
 import {sendCollections, sendGooglePhotosCount, sendGooglePhotosPhotos, sendImageCounts, sendLocalImageData, sendLocalImages, sendVisible} from '../iframe_api.js';
 import {WallpaperCollection, WallpaperImage, WallpaperProviderInterface} from '../personalization_app.mojom-webui.js';
@@ -293,21 +294,19 @@
     sendImageCountsFunction(iframe.contentWindow!, counts);
   }
 
-  /**
-   * Invoked on changes to the list of Google Photos photos.
-   */
+  /** Invoked on changes to the list of Google Photos photos. */
   private async onGooglePhotosChanged_(
-      googlePhotos: unknown[], googlePhotosLoading: boolean) {
+      googlePhotos: Url[]|null, googlePhotosLoading: boolean) {
     if (googlePhotosLoading || !isNullOrArray(googlePhotos)) {
       return;
     }
     const iframe = await this.iframePromise_;
-    sendGooglePhotosPhotosFunction(iframe.contentWindow!, googlePhotos);
+    sendGooglePhotosPhotosFunction(
+        iframe.contentWindow!,
+        googlePhotos?.slice(0, kMaximumGooglePhotosPreviews) ?? null);
   }
 
-  /**
-   * Invoked on changes to the count of Google Photos photos.
-   */
+  /** Invoked on changes to the count of Google Photos photos. */
   private async onGooglePhotosCountChanged_(
       googlePhotosCount: number|null, googlePhotosCountLoading: boolean) {
     if (googlePhotosCountLoading || !isNullOrNumber(googlePhotosCount)) {
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
index 03a2cd9..4fccdc8 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_controller.ts
@@ -5,6 +5,7 @@
 import {assert} from 'chrome://resources/js/assert.m.js';
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {isNonEmptyArray} from '../../common/utils.js';
 import {WallpaperCollection, WallpaperImage, WallpaperLayout, WallpaperProviderInterface, WallpaperType} from '../personalization_app.mojom-webui.js';
@@ -119,11 +120,24 @@
 
   // TODO(dmblack): Create and wire up mojo API. For now, simulate an async
   // request that returns a list of 1,000 Google Photos photos.
-  return new Promise(resolve => setTimeout(() => {
-                       store.dispatch(action.setGooglePhotosPhotosAction(
-                           Array.from({length: 1000})));
-                       resolve();
-                     }, 1000));
+  return new Promise(
+      resolve => setTimeout(() => {
+        // Temporarily use hard-coded URLs from the solid colors backdrop
+        // collection since the backdrop server is already allowlisted with the
+        // untrusted iframe's content security policy.
+        const urls = [
+          'https://lh6.googleusercontent.com/proxy/dVgC6TzmRH-4uhdcqZK37RyRErOz46Y4S9W8Pw3tfRHyPluwELODHfvrx-SorsUFq5YphXy1VXIxQO2oXlF7GfjeRLY4hsH9c20FqCM4Tpk',
+          'https://lh6.googleusercontent.com/proxy/qcBQd3OJ8qwQeFvAb0p23WJau6s1w5RQ0UAUFD1bm56SVBgP1X7-LAfv2_uF47-9Dd6v_fCVKYVU6SCsorTxMRahBSdv6of9FBdReaoqPg',
+          'https://lh6.googleusercontent.com/proxy/fahpL4TekPUgLhKJQ289ISWz_FPG9XutzfjqBiSdDhxjuBfZ7SjlE4j58rg9wzEsu9NcQ0Yrm0B5NW_MWaLbX0TWJ5yRDVH1z-Zf',
+          'https://lh6.googleusercontent.com/proxy/dVgC6TzmRH-4uhdcqZK37RyRErOz46Y4S9W8Pw3tfRHyPluwELODHfvrx-SorsUFq5YphXy1VXIxQO2oXlF7GfjeRLY4hsH9c20FqCM4Tpk',
+          'https://lh6.googleusercontent.com/proxy/5ftru2Wt8g3R7r4TzRAOhJD7jMpLWOiqKxgql3vd_s26EnV51M5WfJe-ZJZkrMnqbOQ4uB1iBycwwGziEVYCwMeRx2Tcdmiq2lH44hUD3OLX',
+          'https://lh6.googleusercontent.com/proxy/qcBQd3OJ8qwQeFvAb0p23WJau6s1w5RQ0UAUFD1bm56SVBgP1X7-LAfv2_uF47-9Dd6v_fCVKYVU6SCsorTxMRahBSdv6of9FBdReaoqPg',
+        ];
+        store.dispatch(action.setGooglePhotosPhotosAction(
+            Array.from({length: 1000})
+                .map((_, i) => ({url: urls[i % urls.length]}))));
+        resolve();
+      }, 1000));
 }
 
 /** Get list of local images from disk and save it to the store. */
diff --git a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts
index 3fb2576b..1ff396f 100644
--- a/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts
+++ b/ash/webui/personalization_app/resources/trusted/wallpaper/wallpaper_state.ts
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 import {FilePath} from 'chrome://resources/mojo/mojo/public/mojom/base/file_path.mojom-webui.js';
+import {Url} from 'chrome://resources/mojo/url/mojom/url.mojom-webui.js';
 
 import {CurrentWallpaper, WallpaperCollection, WallpaperImage} from '../personalization_app.mojom-webui.js';
 
@@ -29,7 +30,7 @@
 export interface GooglePhotosState {
   count: number|null|undefined;
   albums: WallpaperCollection[]|null|undefined;
-  photos: unknown[]|null|undefined;
+  photos: Url[]|null|undefined;
   photosByAlbumId: Record<string, unknown[]|null|undefined>;
 }
 
diff --git a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
index ac68d19..dee9140 100644
--- a/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
+++ b/ash/webui/personalization_app/resources/untrusted/collections_grid.ts
@@ -9,7 +9,7 @@
 import {afterNextRender, html, PolymerElement} from 'chrome-untrusted://personalization/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {loadTimeData} from 'chrome-untrusted://resources/js/load_time_data.m.js';
 
-import {Events, EventType, kMaximumLocalImagePreviews} from '../common/constants.js';
+import {Events, EventType, kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from '../common/constants.js';
 import {getLoadingPlaceholderAnimationDelay, getNumberOfGridItemsPerRow, isNullOrArray, isNullOrNumber, isSelectionEvent} from '../common/utils.js';
 import {selectCollection, selectGooglePhotosCollection, selectLocalCollection, validateReceivedData} from '../untrusted/iframe_api.js';
 
@@ -103,16 +103,14 @@
   }
 }
 
-/**
- * Returns the tile to display for the Google Photos collection.
- */
+/** Returns the tile to display for the Google Photos collection. */
 function getGooglePhotosTile(
-    _: unknown[], googlePhotosCount: number|null): ImageTile {
+    googlePhotos: Url[]|null, googlePhotosCount: number|null): ImageTile {
   return {
     name: loadTimeData.getString('googlePhotosLabel'),
     id: kGooglePhotosCollectionId,
     count: getCountText(googlePhotosCount ?? 0),
-    preview: [],
+    preview: googlePhotos?.slice(0, kMaximumGooglePhotosPreviews) ?? [],
     type: TileType.IMAGE,
   };
 }
@@ -311,11 +309,10 @@
     });
   }
 
-  /**
-   * Invoked on changes to the list and count of Google Photos photos.
-   */
+  /** Invoked on changes to the list and count of Google Photos photos. */
   private onGooglePhotosLoaded_(
-      googlePhotos: unknown[]|undefined, googlePhotosCount: number|undefined) {
+      googlePhotos: Url[]|null|undefined,
+      googlePhotosCount: number|null|undefined) {
     if (isNullOrArray(googlePhotos) && isNullOrNumber(googlePhotosCount)) {
       const tile = getGooglePhotosTile(googlePhotos, googlePhotosCount);
       this.set('tiles_.1', tile);
diff --git a/ash/wm/desks/desk.cc b/ash/wm/desks/desk.cc
index 06ea15f..b16c10e 100644
--- a/ash/wm/desks/desk.cc
+++ b/ash/wm/desks/desk.cc
@@ -31,6 +31,7 @@
 #include "base/metrics/histogram_functions.h"
 #include "base/strings/stringprintf.h"
 #include "chromeos/ui/base/window_properties.h"
+#include "components/app_restore/full_restore_utils.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/window_tracker.h"
 #include "ui/compositor/layer.h"
@@ -96,6 +97,17 @@
          static_cast<int>(AppType::NON_APP);
 }
 
+// Returns true if `window` is supported by Desks Templates and has an app id.
+bool IsSupportedByDesksTemplatesAndHasAppId(aura::Window* window) {
+  auto* delegate = Shell::Get()->desks_templates_delegate();
+  return delegate && delegate->IsWindowSupportedForDeskTemplate(window) &&
+         !wm::GetTransientParent(window) &&
+         (Shell::Get()
+              ->desks_controller()
+              ->disable_app_id_check_for_desk_templates() ||
+          !full_restore::GetAppId(window).empty());
+}
+
 // Adjusts the z-order stacking of |window_to_fix| in its parent to match its
 // order in the MRU window list. This is done after the window is moved from one
 // desk container to another by means of calling AddChild() which adds it as the
@@ -285,12 +297,10 @@
 void Desk::AddWindowToDesk(aura::Window* window) {
   DCHECK(!base::Contains(windows_, window));
 
-  // Increment `num_supported_windows_` if the window is supported.
-  auto* delegate = Shell::Get()->desks_templates_delegate();
-  if (delegate && delegate->IsWindowSupportedForDeskTemplate(window) &&
-      !wm::GetTransientParent(window)) {
+  // Increment `num_supported_windows_` if the window is supported and has a
+  // Full Restore app id.
+  if (IsSupportedByDesksTemplatesAndHasAppId(window))
     num_supported_windows_++;
-  }
 
   windows_.push_back(window);
   // No need to refresh the mini_views if the destroyed window doesn't show up
@@ -316,11 +326,8 @@
   DCHECK(base::Contains(windows_, window));
 
   // Decrement `num_supported_windows_` if the window was supported.
-  auto* delegate = Shell::Get()->desks_templates_delegate();
-  if (delegate && delegate->IsWindowSupportedForDeskTemplate(window) &&
-      !wm::GetTransientParent(window)) {
+  if (IsSupportedByDesksTemplatesAndHasAppId(window))
     num_supported_windows_--;
-  }
 
   base::Erase(windows_, window);
   // No need to refresh the mini_views if the destroyed window doesn't show up
diff --git a/ash/wm/desks/desk.h b/ash/wm/desks/desk.h
index 6fdf16d..5097de7 100644
--- a/ash/wm/desks/desk.h
+++ b/ash/wm/desks/desk.h
@@ -246,10 +246,10 @@
   int first_day_visited_ = -1;
   int last_day_visited_ = -1;
 
-  // The number of supported windows open on this desk. A window is supported
-  // for the Desks Templates feature if its app type is supported. Used to
-  // disable the save desk as templates button if there are no supported windows
-  // open.
+  // The number of Desks Templates supported windows open on this desk with a
+  // valid Full Restore app id. A window is supported for the Desks Templates
+  // feature if its app type is supported. Used to disable the save desk as
+  // templates button if there are no supported windows open.
   int num_supported_windows_ = 0;
 
   // Tracks whether |this| has been interacted with this week. This value is
diff --git a/ash/wm/desks/desks_controller.h b/ash/wm/desks/desks_controller.h
index 69ce5ef..6276b19 100644
--- a/ash/wm/desks/desks_controller.h
+++ b/ash/wm/desks/desks_controller.h
@@ -97,6 +97,10 @@
     return visible_on_all_desks_windows_;
   }
 
+  bool disable_app_id_check_for_desk_templates() {
+    return disable_app_id_check_for_desk_templates_;
+  }
+
   DeskAnimationBase* animation() const { return animation_.get(); }
 
   // Returns the current |active_desk()| or the soon-to-be active desk if a desk
@@ -300,6 +304,13 @@
   friend class DeskAnimationBase;
   friend class DeskActivationAnimation;
   friend class DeskRemovalAnimation;
+  friend class DesksTemplatesTest;
+
+  void set_disable_app_id_check_for_desk_templates(
+      bool disable_app_id_check_for_desk_templates) {
+    disable_app_id_check_for_desk_templates_ =
+        disable_app_id_check_for_desk_templates;
+  }
 
   void OnAnimationFinished(DeskAnimationBase* animation);
 
@@ -365,6 +376,14 @@
   // mode as a result of desks modifications.
   bool are_desks_being_modified_ = false;
 
+  // In ash unittests, the FullRestoreSaveHandler isn't hooked up so initialized
+  // windows lack an app id. If a window doesn't have a valid app id, then it
+  // won't be tracked by Desk as a supported window and those windows will be
+  // deemed unsupported for Desk Templates. If
+  // `disable_app_id_check_for_desk_templates_` is true, then this check is
+  // omitted so we can test Desk Templates.
+  bool disable_app_id_check_for_desk_templates_ = false;
+
   // Not null if there is an on-going desks animation.
   std::unique_ptr<DeskAnimationBase> animation_;
 
diff --git a/ash/wm/desks/desks_unittests.cc b/ash/wm/desks/desks_unittests.cc
index 704d0ca4..1281f82 100644
--- a/ash/wm/desks/desks_unittests.cc
+++ b/ash/wm/desks/desks_unittests.cc
@@ -4975,31 +4975,6 @@
   EXPECT_TRUE(new_desk_button->GetEnabled());
 }
 
-TEST_F(DesksTest, AddRemoveSupportedWindows) {
-  auto* controller = DesksController::Get();
-
-  // Create a desk other than the default initial desk.
-  NewDesk();
-
-  Desk* desk_1 = controller->desks()[0].get();
-
-  // Create 3 supported windows on desk_1.
-  auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
-  auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
-  auto win2 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
-
-  // Expect `num_supported_windows_` to be 3.
-  EXPECT_EQ(3, desk_1->num_supported_windows());
-
-  // Close the supported windows.
-  win0.reset();
-  win1.reset();
-  win2.reset();
-
-  // Expect `num_supported_windows_` to be 0.
-  EXPECT_EQ(0, desk_1->num_supported_windows());
-}
-
 TEST_F(DesksTest, ZeroStateDeskButtonText) {
   UpdateDisplay("1600x1200");
   EnterOverview();
diff --git a/ash/wm/desks/templates/desks_templates_unittest.cc b/ash/wm/desks/templates/desks_templates_unittest.cc
index 6ac73cc..25cfd89d 100644
--- a/ash/wm/desks/templates/desks_templates_unittest.cc
+++ b/ash/wm/desks/templates/desks_templates_unittest.cc
@@ -8,6 +8,7 @@
 #include "ash/constants/ash_features.h"
 #include "ash/constants/ash_pref_names.h"
 #include "ash/public/cpp/desk_template.h"
+#include "ash/public/cpp/desks_templates_delegate.h"
 #include "ash/public/cpp/rounded_image_view.h"
 #include "ash/session/session_controller_impl.h"
 #include "ash/shell.h"
@@ -45,6 +46,7 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/time/time.h"
 #include "components/app_restore/app_launch_info.h"
+#include "components/app_restore/full_restore_utils.h"
 #include "components/app_restore/window_info.h"
 #include "components/app_restore/window_properties.h"
 #include "components/prefs/pref_service.h"
@@ -307,10 +309,26 @@
       ASSERT_TRUE(overview_grid->IsShowingDesksTemplatesGrid());
   }
 
+  void SetDisableAppIdCheckForDeskTemplates(bool disabled) {
+    DesksController::Get()->set_disable_app_id_check_for_desk_templates(
+        disabled);
+  }
+
   // OverviewTestBase:
   void SetUp() override {
     scoped_feature_list_.InitAndEnableFeature(features::kDesksTemplates);
     OverviewTestBase::SetUp();
+
+    // The FullRestoreSaveHandler isn't setup during tests so every window we
+    // create in tests doesn't have an app id associated with it. Since these
+    // windows don't have app ids, Desk won't consider them supported windows so
+    // we need to disable the app id check during tests.
+    DesksController::Get()->set_disable_app_id_check_for_desk_templates(true);
+  }
+
+  void TearDown() override {
+    DesksController::Get()->set_disable_app_id_check_for_desk_templates(true);
+    OverviewTestBase::TearDown();
   }
 
  protected:
@@ -1200,23 +1218,57 @@
 }
 
 // Tests that the save desk as template button is disabled when all windows on
-// the desk are unsupported.
+// the desk are unsupported or there are no windows with Full Restore app ids.
+// See crbug.com/1277763.
 TEST_F(DesksTemplatesTest, AllUnsupportedAppsDisablesSaveTemplates) {
-  auto* root = Shell::Get()->GetPrimaryRootWindow();
+  SetDisableAppIdCheckForDeskTemplates(false);
 
-  // Use `CreateTestWindow` instead of `CreateAppWindow`, which by default
+  // Use `CreateTestWindow()` instead of `CreateAppWindow()`, which by default
   // creates a supported window.
   auto test_window = CreateTestWindow();
 
+  // Also create an app window which should be supported and not have an app id.
+  auto no_app_id_window = CreateAppWindow();
+  auto* delegate = Shell::Get()->desks_templates_delegate();
+  ASSERT_TRUE(
+      delegate->IsWindowSupportedForDeskTemplate(no_app_id_window.get()));
+  ASSERT_TRUE(full_restore::GetAppId(no_app_id_window.get()).empty());
+
   EXPECT_EQ(0, DesksController::Get()->active_desk()->num_supported_windows());
 
   ToggleOverview();
 
   auto* save_template = static_cast<PillButton*>(
-      GetSaveDeskAsTemplateButtonForRoot(root)->GetContentsView());
+      GetSaveDeskAsTemplateButtonForRoot(Shell::Get()->GetPrimaryRootWindow())
+          ->GetContentsView());
   EXPECT_EQ(views::Button::STATE_DISABLED, save_template->GetState());
 }
 
+TEST_F(DesksTemplatesTest, AddRemoveSupportedWindows) {
+  auto* controller = DesksController::Get();
+
+  // Create a desk other than the default initial desk.
+  NewDesk();
+
+  Desk* desk_1 = controller->desks()[0].get();
+
+  // Create 3 supported windows on desk_1.
+  auto win0 = CreateAppWindow(gfx::Rect(0, 0, 250, 100));
+  auto win1 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
+  auto win2 = CreateAppWindow(gfx::Rect(50, 50, 200, 200));
+
+  // Expect `num_supported_windows_` to be 3.
+  EXPECT_EQ(3, desk_1->num_supported_windows());
+
+  // Close the supported windows.
+  win0.reset();
+  win1.reset();
+  win2.reset();
+
+  // Expect `num_supported_windows_` to be 0.
+  EXPECT_EQ(0, desk_1->num_supported_windows());
+}
+
 // Tests the mouse and touch hover behavior on the template item view.
 TEST_F(DesksTemplatesTest, HoverOnTemplateItemView) {
   auto test_window = CreateAppWindow();
diff --git a/ash/wm/overview/overview_item.cc b/ash/wm/overview/overview_item.cc
index b08bc1c..57f072b6 100644
--- a/ash/wm/overview/overview_item.cc
+++ b/ash/wm/overview/overview_item.cc
@@ -265,9 +265,8 @@
   }
 
   overview_item_view_->OnOverviewItemWindowRestoring();
-  transform_window_.RestoreWindow(
-      reset_transform,
-      /*transform_back=*/!was_desks_templates_grid_showing);
+  transform_window_.RestoreWindow(reset_transform,
+                                  was_desks_templates_grid_showing);
 
   if (transform_window_.IsMinimized()) {
     const auto enter_exit_type = overview_session_->enter_exit_overview_type();
diff --git a/ash/wm/overview/scoped_overview_transform_window.cc b/ash/wm/overview/scoped_overview_transform_window.cc
index 50c2707..3e7b336 100644
--- a/ash/wm/overview/scoped_overview_transform_window.cc
+++ b/ash/wm/overview/scoped_overview_transform_window.cc
@@ -112,7 +112,8 @@
     return;
 
   gfx::Rect new_clip_rect = clip_rect;
-  if (new_clip_rect.IsEmpty() && animator->is_animating()) {
+  if (new_clip_rect.IsEmpty() &&
+      animator->IsAnimatingProperty(ui::LayerAnimationElement::CLIP)) {
     // Animate to a clip the size of |window|. Create a self deleting object
     // which removes the clip when the animation is finished.
     new_clip_rect = gfx::Rect(window->bounds().size());
@@ -162,7 +163,7 @@
     : overview_item_(overview_item),
       window_(window),
       original_opacity_(window->layer()->GetTargetOpacity()),
-      original_clip_rect_(window_->layer()->clip_rect()) {
+      original_clip_rect_(window_->layer()->GetTargetClipRect()) {
   type_ = GetWindowDimensionsType(window->bounds().size());
 
   std::vector<aura::Window*> transient_children_to_hide;
@@ -257,13 +258,14 @@
   return OverviewGridWindowFillMode::kNormal;
 }
 
-void ScopedOverviewTransformWindow::RestoreWindow(bool reset_transform,
-                                                  bool animate_back) {
+void ScopedOverviewTransformWindow::RestoreWindow(
+    bool reset_transform,
+    bool was_desk_templates_grid_showing) {
   // Shadow controller may be null on shutdown.
   if (Shell::Get()->shadow_controller())
     Shell::Get()->shadow_controller()->UpdateShadowForWindow(window_);
 
-  if (IsMinimized() || !animate_back) {
+  if (IsMinimized() || was_desk_templates_grid_showing) {
     // Minimized windows may have had their transforms altered by swiping up
     // from the shelf.
     SetTransform(window_, gfx::Transform());
@@ -297,8 +299,11 @@
     }
   }
 
-  ScopedOverviewAnimationSettings animation_settings(
-      overview_item_->GetExitOverviewAnimationType(), window_);
+  OverviewAnimationType animation_type =
+      was_desk_templates_grid_showing
+          ? OVERVIEW_ANIMATION_NONE
+          : overview_item_->GetExitOverviewAnimationType();
+  ScopedOverviewAnimationSettings animation_settings(animation_type, window_);
   SetOpacity(original_opacity_);
   SetClipping({ClippingType::kExit, gfx::SizeF()});
 }
@@ -453,14 +458,14 @@
           // We also do not consider `top_view_inset` in our calculation of
           // `new_scale` because we want to find out the height of the inset when
           // the whole window, including the inset, is scaled down to `new_bounds`.
-          const float new_scale = 
+          const float new_scale =
               GetItemScale(rect.size(), new_bounds.size(), 0, 0);
           const float scaled_top_view_inset = top_view_inset * new_scale;
-          // Offset `new_bounds` to be at a point in the overview item frame where 
-          // it will be centered when we clip the `top_view_inset`.
-          new_bounds.Offset(0, (bounds.height() - title_height) / 2 - 
-                               (new_height - scaled_top_view_inset) / 2 - 
-                               scaled_top_view_inset);
+          // Offset `new_bounds` to be at a point in the overview item frame
+          // where it will be centered when we clip the `top_view_inset`.
+          new_bounds.Offset(0, (bounds.height() - title_height) / 2 -
+                                   (new_height - scaled_top_view_inset) / 2 -
+                                   scaled_top_view_inset);
         } else {
           new_bounds.ClampToCenteredSize(gfx::SizeF(bounds.width(), new_height));
         }
diff --git a/ash/wm/overview/scoped_overview_transform_window.h b/ash/wm/overview/scoped_overview_transform_window.h
index 0907741..b17ac49 100644
--- a/ash/wm/overview/scoped_overview_transform_window.h
+++ b/ash/wm/overview/scoped_overview_transform_window.h
@@ -99,7 +99,8 @@
   // If |reset_transform| equals false, the window's transform will not be reset
   // to identity transform when exiting the overview mode. See
   // OverviewItem::RestoreWindow() for details why we need this.
-  void RestoreWindow(bool reset_transform, bool animate_back = true);
+  void RestoreWindow(bool reset_transform,
+                     bool was_desk_templates_grid_showing);
 
   // Prepares for overview mode by doing any necessary actions before entering.
   void PrepareForOverview();
diff --git a/base/allocator/partition_allocator/starscan/pcscan_internal.cc b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
index c5bb7dd..90ab8a1 100644
--- a/base/allocator/partition_allocator/starscan/pcscan_internal.cc
+++ b/base/allocator/partition_allocator/starscan/pcscan_internal.cc
@@ -263,7 +263,7 @@
 #if defined(PA_STARSCAN_NEON_SUPPORTED)
   return SimdSupport::kNEON;
 #else
-  base::CPU cpu;
+  const base::CPU& cpu = base::CPU::GetInstanceNoAllocation();
   if (cpu.has_avx2())
     return SimdSupport::kAVX2;
   if (cpu.has_sse41())
diff --git a/build/android/test_wrapper/logdog_wrapper.py b/build/android/test_wrapper/logdog_wrapper.py
index 303e288..ac1542fb 100755
--- a/build/android/test_wrapper/logdog_wrapper.py
+++ b/build/android/test_wrapper/logdog_wrapper.py
@@ -13,6 +13,8 @@
 import subprocess
 import sys
 
+import six
+
 _SRC_PATH = os.path.abspath(os.path.join(
     os.path.dirname(__file__), '..', '..', '..'))
 sys.path.append(os.path.join(_SRC_PATH, 'third_party', 'catapult', 'devil'))
@@ -33,7 +35,10 @@
 
 def CommandParser():
   # Parses the command line arguments being passed in
-  parser = argparse.ArgumentParser()
+  if six.PY3:
+    parser = argparse.ArgumentParser(allow_abbrev=False)
+  else:
+    parser = argparse.ArgumentParser()
   wrapped = parser.add_mutually_exclusive_group()
   wrapped.add_argument(
       '--target',
diff --git a/build/fuchsia/linux.sdk.sha1 b/build/fuchsia/linux.sdk.sha1
index da0eeae..1220bba0 100644
--- a/build/fuchsia/linux.sdk.sha1
+++ b/build/fuchsia/linux.sdk.sha1
@@ -1 +1 @@
-7.20211221.1.1
+7.20211221.3.1
diff --git a/build/fuchsia/mac.sdk.sha1 b/build/fuchsia/mac.sdk.sha1
index da0eeae..1220bba0 100644
--- a/build/fuchsia/mac.sdk.sha1
+++ b/build/fuchsia/mac.sdk.sha1
@@ -1 +1 @@
-7.20211221.1.1
+7.20211221.3.1
diff --git a/chrome/android/chrome_java_resources.gni b/chrome/android/chrome_java_resources.gni
index 220effc..1824e362 100644
--- a/chrome/android/chrome_java_resources.gni
+++ b/chrome/android/chrome_java_resources.gni
@@ -531,6 +531,7 @@
   "java/res/drawable/material_tooltip_background.xml",
   "java/res/drawable/mir_card.xml",
   "java/res/drawable/outline_chevron_right_24dp.xml",
+  "java/res/drawable/pill_background.xml",
   "java/res/drawable/price_tracking_disabled.xml",
   "java/res/drawable/price_tracking_enabled_filled.xml",
   "java/res/drawable/price_tracking_enabled_outline.xml",
@@ -650,6 +651,7 @@
   "java/res/layout/fre_data_reduction_proxy_lite_mode.xml",
   "java/res/layout/fre_tos_privacy_disclaimer.xml",
   "java/res/layout/fre_tosanduma.xml",
+  "java/res/layout/fullscreen_notification.xml",
   "java/res/layout/history_clear_browsing_data_header.xml",
   "java/res/layout/history_item_view.xml",
   "java/res/layout/history_main.xml",
diff --git a/chrome/android/features/cablev2_authenticator/BUILD.gn b/chrome/android/features/cablev2_authenticator/BUILD.gn
index 237bb8d..aeb66eb 100644
--- a/chrome/android/features/cablev2_authenticator/BUILD.gn
+++ b/chrome/android/features/cablev2_authenticator/BUILD.gn
@@ -54,6 +54,7 @@
     "java/res/drawable/cable_qr_code.xml",
     "java/res/drawable/usb_conn_disconnect.xml",
     "java/res/layout-sw600dp/cablev2_ble_enable.xml",
+    "java/res/layout-sw600dp/cablev2_qr.xml",
     "java/res/layout/cablev2_ble_enable.xml",
     "java/res/layout/cablev2_qr.xml",
     "java/res/layout/cablev2_usb_attached.xml",
diff --git a/chrome/android/features/cablev2_authenticator/java/res/layout-sw600dp/cablev2_qr.xml b/chrome/android/features/cablev2_authenticator/java/res/layout-sw600dp/cablev2_qr.xml
new file mode 100644
index 0000000..bb0e9a1
--- /dev/null
+++ b/chrome/android/features/cablev2_authenticator/java/res/layout-sw600dp/cablev2_qr.xml
@@ -0,0 +1,120 @@
+<?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. -->
+
+<ScrollView
+    xmlns:a="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    a:layout_width="match_parent"
+    a:layout_height="match_parent"
+    a:fillViewport="true"
+    a:scrollbars="none">
+
+  <LinearLayout
+      a:layout_width="match_parent"
+      a:layout_height="wrap_content"
+      a:orientation="vertical">
+
+    <TextView
+        a:layout_width="match_parent"
+        a:layout_height="wrap_content"
+        a:layout_marginTop="104dp"
+        a:layout_marginLeft="24dp"
+        a:layout_marginRight="24dp"
+        a:gravity="center_horizontal"
+        a:text="@string/cablev2_qr_title"
+        a:textSize="24sp" />
+
+    <!-- This is a semantically-meaningless picture of a phone and BLE icon that
+    screen readers can ignore. Thus contentDescription is null.
+
+    The visual center of this image isn't the pixel-center, and thus a left margin
+    is used to tweak the horizontal centering. The tooling thinks that's an RTL
+    issue, but RTL mode doesn't reflect images so the padding still needs to
+    be on the left. tools:ignore is used to skip this warning. -->
+    <ImageView
+        a:layout_width="match_parent"
+        a:layout_height="212dp"
+        a:layout_marginTop="52dp"
+        a:layout_marginLeft="40dp"
+        a:layout_marginBottom="35dp"
+        a:contentDescription="@null"
+        a:gravity="center_horizontal"
+        a:src="@drawable/cable_qr_code"
+        tools:ignore="RtlHardcoded"/>
+
+    <TextView
+        a:layout_width="wrap_content"
+        a:layout_height="wrap_content"
+        style="@style/TextAppearance.TextMedium.Primary"
+        a:layout_marginTop="52dp"
+        a:layout_marginLeft="24dp"
+        a:layout_marginRight="24dp"
+        a:layout_gravity="center_horizontal"
+        a:padding="0px"
+        a:text="@string/cablev2_qr_body_tablet"
+        a:textSize="14sp" />
+
+    <LinearLayout
+        a:layout_width="match_parent"
+        a:layout_height="wrap_content"
+        a:orientation="horizontal"
+        a:gravity="center_vertical"
+        a:padding="24dp">
+
+      <CheckBox
+          a:id="@+id/qr_link"
+          a:checked="true"
+          a:layout_width="wrap_content"
+          a:layout_height="wrap_content" />
+
+      <TextView
+          a:layout_width="wrap_content"
+          a:layout_height="wrap_content"
+          style="@style/TextAppearance.TextMedium.Primary"
+          a:text="@string/cablev2_qr_remember" />
+    </LinearLayout>
+
+    <!-- This consumes all extra vertical space and so bottom-aligns everything
+         that follows. -->
+    <View
+      a:layout_width="0dp"
+      a:layout_height="0dp"
+      a:layout_weight="1"
+      a:visibility="invisible"/>
+
+    <LinearLayout
+        a:layout_width="match_parent"
+        a:layout_height="wrap_content"
+        a:orientation="horizontal"
+        a:paddingLeft="24dp"
+        a:paddingRight="24dp"
+        a:paddingBottom="16dp">
+
+      <org.chromium.ui.widget.ButtonCompat
+          a:id="@+id/qr_reject"
+          style="@style/TextButton"
+          a:layout_gravity="start"
+          a:layout_width="wrap_content"
+          a:layout_height="wrap_content"
+          a:text="@string/cablev2_qr_dont_allow" />
+
+      <!-- This consumes all extra horizontal space and so right-aligns everything
+           that follows. -->
+      <View
+        a:layout_width="0dp"
+        a:layout_height="0dp"
+        a:layout_weight="1"
+        a:visibility="invisible"/>
+
+      <org.chromium.ui.widget.ButtonCompat
+          a:id="@+id/qr_connect"
+          style="@style/FilledButton.Flat"
+          a:layout_gravity="end"
+          a:layout_width="wrap_content"
+          a:layout_height="wrap_content"
+          a:text="@string/cablev2_qr_allow" />
+    </LinearLayout>
+  </LinearLayout>
+</ScrollView>
diff --git a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_qr.xml b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_qr.xml
index 5451a83..04e0eff 100644
--- a/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_qr.xml
+++ b/chrome/android/features/cablev2_authenticator/java/res/layout/cablev2_qr.xml
@@ -23,8 +23,7 @@
         a:layout_marginLeft="24dp"
         a:layout_marginRight="24dp"
         a:gravity="center_horizontal"
-        tools:ignore="HardcodedText"
-        a:text="Connect your device with QR Code?"
+        a:text="@string/cablev2_qr_title"
         a:textSize="24sp" />
 
     <!-- This is a semantically-meaningless picture of a phone and BLE icon that
@@ -54,14 +53,14 @@
         a:layout_marginRight="24dp"
         a:layout_gravity="center_horizontal"
         a:padding="0px"
-        tools:ignore="HardcodedText"
-        a:text="The computer displaying this QR Code is asking to use this device for signing in."
+        a:text="@string/cablev2_qr_body_phone"
         a:textSize="14sp" />
 
     <LinearLayout
         a:layout_width="match_parent"
         a:layout_height="wrap_content"
         a:orientation="horizontal"
+        a:gravity="center_vertical"
         a:padding="24dp">
 
       <CheckBox
@@ -74,8 +73,7 @@
           a:layout_width="wrap_content"
           a:layout_height="wrap_content"
           style="@style/TextAppearance.TextMedium.Primary"
-          tools:ignore="HardcodedText"
-          a:text="Remember this device" />
+          a:text="@string/cablev2_qr_remember" />
     </LinearLayout>
 
     <!-- This consumes all extra vertical space and so bottom-aligns everything
@@ -100,8 +98,7 @@
           a:layout_gravity="start"
           a:layout_width="wrap_content"
           a:layout_height="wrap_content"
-          tools:ignore="HardcodedText"
-          a:text="Don't allow" />
+          a:text="@string/cablev2_qr_dont_allow" />
 
       <!-- This consumes all extra horizontal space and so right-aligns everything
            that follows. -->
@@ -117,8 +114,7 @@
           a:layout_gravity="end"
           a:layout_width="wrap_content"
           a:layout_height="wrap_content"
-          tools:ignore="HardcodedText"
-          a:text="Allow" />
+          a:text="@string/cablev2_qr_allow" />
     </LinearLayout>
   </LinearLayout>
 </ScrollView>
diff --git a/chrome/android/java/res/drawable/pill_background.xml b/chrome/android/java/res/drawable/pill_background.xml
new file mode 100644
index 0000000..b9ed6ad
--- /dev/null
+++ b/chrome/android/java/res/drawable/pill_background.xml
@@ -0,0 +1,14 @@
+<?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. -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:dither="true"
+    android:shape="rectangle">
+    <corners android:radius="40dp" />
+    <solid android:color="@color/default_bg_color_dark"/>
+    <size
+        android:width="60dp"
+        android:height="20dp" />
+</shape>
diff --git a/chrome/android/java/res/layout/fullscreen_notification.xml b/chrome/android/java/res/layout/fullscreen_notification.xml
new file mode 100644
index 0000000..5687b66a
--- /dev/null
+++ b/chrome/android/java/res/layout/fullscreen_notification.xml
@@ -0,0 +1,26 @@
+<?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. -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:paddingTop="24dp">
+    <TextView
+        android:id="@+id/text"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:maxWidth="@dimen/fullscreen_notification_max_width"
+        android:layout_gravity="center"
+        android:background="@drawable/pill_background"
+        android:textAppearance="@style/TextAppearance.Toast"
+        android:paddingStart="20dp"
+        android:paddingEnd="20dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp"
+        android:textAlignment="center"
+        android:text="@string/immersive_fullscreen_api_notification"/>
+</LinearLayout>
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 6cbd92c..b2547931 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -157,6 +157,7 @@
     <!-- Should match toolbar_height_no_shadow -->
     <dimen name="control_container_height">56dp</dimen>
     <dimen name="custom_tabs_control_container_height">56dp</dimen>
+    <dimen name="fullscreen_notification_max_width">320dp</dimen>
 
     <!-- The combined height of the tab strip and toolbar. -->
     <dimen name="tab_strip_and_toolbar_height">56dp</dimen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
index 91a8114..ab567df 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ChromeContextMenuPopulator.java
@@ -80,9 +80,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 /**
  * A {@link ContextMenuPopulator} used for showing the default Chrome context menu.
@@ -262,15 +260,6 @@
                     ContextMenuUtils.getContextMenuTypeForHistogram(params));
             RecordHistogram.recordEnumeratedHistogram(histogramName, action, Action.NUM_ENTRIES);
 
-            if (!params.isVideo() && params.isImage()
-                    && LensUtils.isInShoppingAllowlist(params.getPageUrl())) {
-                String shoppingHistogramName = params.isAnchor()
-                        ? "ContextMenu.SelectedOptionAndroid.ImageLink.ShoppingDomain"
-                        : "ContextMenu.SelectedOptionAndroid.Image.ShoppingDomain";
-                RecordHistogram.recordEnumeratedHistogram(
-                        shoppingHistogramName, action, Action.NUM_ENTRIES);
-            }
-
             if (params.isAnchor() && !params.isVideo() && !params.getOpenedFromHighlight()) {
                 if (params.isImage()) {
                     assert histogramName.equals("ContextMenu.SelectedOptionAndroid.ImageLink");
@@ -493,7 +482,6 @@
             ModelList imageGroup = new ModelList();
             boolean isSrcDownloadableScheme =
                     UrlUtilities.isDownloadableScheme(mParams.getSrcUrl());
-            boolean showLensShoppingMenuItem = false;
             // Avoid showing open image option for same image which is already opened.
             if (mMode == ContextMenuMode.CUSTOM_TAB
                     && !mItemDelegate.getPageUrl().equals(mParams.getSrcUrl())) {
@@ -528,20 +516,16 @@
                 if (checkSupportsGoogleSearchByImage(isSrcDownloadableScheme)) {
                     // All behavior relating to Lens integration is gated by Feature Flag.
                     // A map to indicate which image search menu item would be shown.
-                    Map<String, Boolean> imageSearchMenuItemsToShow =
-                            getSearchByImageMenuItemsToShowAndRecordMetrics(
+                    boolean shouldShowSearchImageWithLens =
+                            shouldShowSearchWithLensAndRecordMetrics(
                                     mParams.getPageUrl(), mItemDelegate.isIncognito());
-                    if (imageSearchMenuItemsToShow.get(LENS_SEARCH_MENU_ITEM_KEY)) {
+                    if (shouldShowSearchImageWithLens) {
                         imageGroup.add(createListItem(Item.SEARCH_WITH_GOOGLE_LENS, true));
                         maybeRecordUkmLensShown();
-                    } else if (imageSearchMenuItemsToShow.get(SEARCH_BY_IMAGE_MENU_ITEM_KEY)) {
+                    } else {
                         imageGroup.add(createListItem(Item.SEARCH_BY_IMAGE));
                         maybeRecordUkmSearchByImageShown();
                     }
-                    // Check whether we should show Lens Shopping menu item.
-                    if (imageSearchMenuItemsToShow.get(LENS_SHOP_MENU_ITEM_KEY)) {
-                        showLensShoppingMenuItem = true;
-                    }
                 } else if (ChromeFeatureList.isEnabled(
                                    ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS)) {
                     LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
@@ -555,12 +539,6 @@
                 imageGroup.add(createShareListItem(Item.SHARE_IMAGE, Item.DIRECT_SHARE_IMAGE));
             }
 
-            // Show Lens Shopping Menu Item when the Lens Shopping feature is supported.
-            if (showLensShoppingMenuItem) {
-                imageGroup.add(createListItem(Item.SHOP_IMAGE_WITH_GOOGLE_LENS, true));
-                maybeRecordUkmLensShoppingShown();
-            }
-
             recordSaveImageContextMenuResult(isSrcDownloadableScheme);
             groupedItems.add(new Pair<>(R.string.contextmenu_image_title, imageGroup));
         }
@@ -775,12 +753,6 @@
         } else if (itemId == R.id.contextmenu_search_by_image) {
             recordContextMenuSelection(ContextMenuUma.Action.SEARCH_BY_IMAGE);
             mNativeDelegate.searchForImage();
-        } else if (itemId == R.id.contextmenu_shop_image_with_google_lens) {
-            recordContextMenuSelection(ContextMenuUma.Action.SHOP_IMAGE_WITH_GOOGLE_LENS);
-            searchWithGoogleLens(LensEntryPoint.CONTEXT_MENU_SHOP_MENU_ITEM);
-            SharedPreferencesManager prefManager = SharedPreferencesManager.getInstance();
-            prefManager.writeBoolean(
-                    ChromePreferenceKeys.CONTEXT_MENU_SHOP_IMAGE_WITH_GOOGLE_LENS_CLICKED, true);
         } else if (itemId == R.id.contextmenu_share_image) {
             recordContextMenuSelection(ContextMenuUma.Action.SHARE_IMAGE);
             shareImage();
@@ -1014,33 +986,19 @@
      * @param pageUrl The Url associated with the main frame of the page that triggered the context
      *         menu.
      * @param isIncognito Whether the user is incognito.
-     * @return An immutable map. Can be used to check whether a specific Lens menu item is enabled.
+     * @return A boolean. True if "Search image with Google Lens" should be enabled, otherwise
+     *         False.
      */
-    private Map<String, Boolean> getSearchByImageMenuItemsToShowAndRecordMetrics(
-            GURL pageUrl, boolean isIncognito) {
+    private boolean shouldShowSearchWithLensAndRecordMetrics(GURL pageUrl, boolean isIncognito) {
         // If Google Lens feature is not supported, show search by image menu item.
         if (!LensUtils.isGoogleLensFeatureEnabled(isIncognito)) {
-            // TODO(yusuyoutube): Cleanup. Remove repetition.
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, false);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, true);
-                }
-            });
+            return false;
         }
 
         if (isTabletScreen() && !LensUtils.isGoogleLensFeatureEnabledOnTablet()) {
             LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
                     LensMetrics.LensSupportStatus.DISABLED_ON_TABLET);
-
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, false);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, true);
-                }
-            });
+            return false;
         }
 
         final TemplateUrlService templateUrlServiceInstance = getTemplateUrlService();
@@ -1048,107 +1006,35 @@
         if (!templateUrlServiceInstance.isDefaultSearchEngineGoogle()) {
             LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
                     LensMetrics.LensSupportStatus.NON_GOOGLE_SEARCH_ENGINE);
-
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, false);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, true);
-                }
-            });
+            return false;
         }
         if (TextUtils.isEmpty(versionName)) {
             LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
                     LensMetrics.LensSupportStatus.ACTIVITY_NOT_ACCESSIBLE);
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, false);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, true);
-                }
-            });
+            return false;
         }
         if (GSAState.getInstance(mContext).isAgsaVersionBelowMinimum(
                     versionName, LensUtils.getMinimumAgsaVersionForLensSupport())) {
             LensMetrics.recordLensSupportStatus(
                     LENS_SUPPORT_STATUS_HISTOGRAM_NAME, LensMetrics.LensSupportStatus.OUT_OF_DATE);
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, false);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, true);
-                }
-            });
+            return false;
         }
 
         if (LensUtils.isDeviceOsBelowMinimum()) {
             LensMetrics.recordLensSupportStatus(
                     LENS_SUPPORT_STATUS_HISTOGRAM_NAME, LensMetrics.LensSupportStatus.LEGACY_OS);
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, false);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, true);
-                }
-            });
+            return false;
         }
 
         if (!LensUtils.isValidAgsaPackage(mExternalAuthUtils)) {
             LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
                     LensMetrics.LensSupportStatus.INVALID_PACKAGE);
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, false);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, true);
-                }
-            });
-        }
-
-        // In Lens Shopping Menu Item experiment, fallback to Search image with Google Lens
-        // When the url is not in domain allowlist and AGSA version is equal to or greater than the
-        // minimum shopping supported version.
-        if (LensUtils.isGoogleLensShoppingFeatureEnabled(isIncognito)
-                && !GSAState.getInstance(mContext).isAgsaVersionBelowMinimum(
-                        versionName, LensUtils.getMinimumAgsaVersionForLensShoppingSupport())
-                && LensUtils.isInShoppingAllowlist(pageUrl)) {
-            // Show both search and shop menu items when experiment with both Lens searching and
-            // shopping.
-            if (ChromeFeatureList.isEnabled(
-                        ChromeFeatureList.CONTEXT_MENU_SEARCH_AND_SHOP_WITH_GOOGLE_LENS)) {
-                LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
-                        LensMetrics.LensSupportStatus.LENS_SHOP_AND_SEARCH_SUPPORTED);
-                return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                    {
-                        put(LENS_SEARCH_MENU_ITEM_KEY, true);
-                        put(LENS_SHOP_MENU_ITEM_KEY, true);
-                        put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, false);
-                    }
-                });
-            }
-
-            // Hide Search With Google Lens menu item when experiment only with Lens Shopping
-            // menu items.
-            LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
-                    LensMetrics.LensSupportStatus.LENS_SHOP_SUPPORTED);
-            return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-                {
-                    put(LENS_SEARCH_MENU_ITEM_KEY, false);
-                    put(LENS_SHOP_MENU_ITEM_KEY, true);
-                    put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, false);
-                }
-            });
+            return false;
         }
 
         LensMetrics.recordLensSupportStatus(LENS_SUPPORT_STATUS_HISTOGRAM_NAME,
                 LensMetrics.LensSupportStatus.LENS_SEARCH_SUPPORTED);
-        return Collections.unmodifiableMap(new HashMap<String, Boolean>() {
-            {
-                put(LENS_SEARCH_MENU_ITEM_KEY, true);
-                put(LENS_SHOP_MENU_ITEM_KEY, false);
-                put(SEARCH_BY_IMAGE_MENU_ITEM_KEY, false);
-            }
-        });
+        return true;
     }
 
     private ListItem createListItem(@Item int item) {
@@ -1208,16 +1094,6 @@
         }
     }
 
-    /**
-     * If not disabled record a UKM for opening the context menu with the lens shopping
-     * item.
-     */
-    private void maybeRecordUkmLensShoppingShown() {
-        if (LensUtils.shouldLogUkmByFeature(ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS)) {
-            maybeRecordBooleanUkm("ContextMenuAndroid.Shown", "ShopWithGoogleLens");
-        }
-    }
-
     private void maybeRecordUkmLensChipShown(int chipType) {
         String actionName = null;
         switch (chipType) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
index c532989d..ca97522 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextmenu/ContextMenuHelper.java
@@ -16,7 +16,6 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
 import org.chromium.chrome.browser.performance_hints.PerformanceHintsObserver;
-import org.chromium.chrome.browser.share.LensUtils;
 import org.chromium.components.embedder_support.contextmenu.ContextMenuParams;
 import org.chromium.content_public.browser.RenderFrameHost;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
@@ -124,10 +123,6 @@
                     TimeUnit.MICROSECONDS.toMillis(TimeUtilsJni.get().getTimeTicksNowUs());
             RecordHistogram.recordBooleanHistogram("ContextMenu.Shown", mWebContents != null);
             recordContextMenuShownType(params);
-            if (LensUtils.isInShoppingAllowlist(mCurrentContextMenuParams.getPageUrl())) {
-                RecordHistogram.recordBooleanHistogram(
-                        "ContextMenu.Shown.ShoppingDomain", mWebContents != null);
-            }
             if (sMenuShownCallbackForTests != null) {
                 sMenuShownCallbackForTests.onResult((ContextMenuCoordinator) mCurrentContextMenu);
             }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
index 0354a90d..73e3b4f7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/fullscreen/FullscreenHtmlApiHandler.java
@@ -11,9 +11,10 @@
 import android.app.Activity;
 import android.os.Handler;
 import android.os.Message;
-import android.view.Gravity;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.OnLayoutChangeListener;
+import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
 
@@ -46,7 +47,6 @@
 import org.chromium.content_public.browser.NavigationHandle;
 import org.chromium.content_public.browser.SelectionPopupController;
 import org.chromium.content_public.browser.WebContents;
-import org.chromium.ui.widget.Toast;
 
 import java.lang.ref.WeakReference;
 
@@ -66,6 +66,8 @@
     // Delay to allow a frame to render between getting the fullscreen layout update and clearing
     // the SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN flag.
     private static final long CLEAR_LAYOUT_FULLSCREEN_DELAY_MS = 20;
+    // Fade in/out animation duration for fullscreen notification toast.
+    private static final int TOAST_FADE_MS = 500;
 
     private final Activity mActivity;
     private final Handler mHandler;
@@ -86,7 +88,7 @@
 
     // Toast at the top of the screen that is shown when user enters fullscreen for the
     // first time.
-    private Toast mNotificationToast;
+    private View mNotificationToast;
 
     private OnLayoutChangeListener mFullscreenOnLayoutChangeListener;
 
@@ -509,23 +511,27 @@
      * Create and show the fullscreen notification toast.
      */
     private void showNotificationToast() {
-        if (mNotificationToast != null) {
-            mNotificationToast.cancel();
-        }
-        int resId = R.string.immersive_fullscreen_api_notification;
-        mNotificationToast = Toast.makeText(mActivity, resId, Toast.LENGTH_LONG);
-        mNotificationToast.setGravity(Gravity.TOP | Gravity.CENTER, 0, 0);
-        mNotificationToast.show();
+        assert mTab != null && mTab.getContentView() != null;
+        ViewGroup parent = mTab.getContentView();
+        if (mNotificationToast != null) parent.removeView(mNotificationToast);
+        mNotificationToast =
+                LayoutInflater.from(mActivity).inflate(R.layout.fullscreen_notification, null);
+        mNotificationToast.setAlpha(0);
+        parent.addView(mNotificationToast);
+        mNotificationToast.animate().alpha(1).setDuration(TOAST_FADE_MS).start();
+        mHandler.postDelayed(this::hideNotificationToast, 5000);
     }
 
     /**
      * Hides the notification toast.
      */
     private void hideNotificationToast() {
-        if (mNotificationToast != null) {
-            mNotificationToast.cancel();
+        if (mNotificationToast == null) return;
+        mNotificationToast.animate().alpha(0).setDuration(TOAST_FADE_MS).withEndAction(() -> {
+            assert mTab != null && mTab.getContentView() != null;
+            mTab.getContentView().removeView(mNotificationToast);
             mNotificationToast = null;
-        }
+        });
     }
 
     // ActivityStateListener
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java
index 3b9d973..b80995b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omaha/RequestGenerator.java
@@ -14,6 +14,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import org.chromium.base.BuildInfo;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.uid.SettingsSecureBasedIdentificationGenerator;
 import org.chromium.chrome.browser.uid.UniqueIdentificationGeneratorFactory;
 import org.chromium.ui.base.DeviceFormFactor;
@@ -80,8 +81,12 @@
             serializer.attribute(null, "requestid", "{" + data.getRequestID() + "}");
             serializer.attribute(null, "sessionid", "{" + sessionID + "}");
             serializer.attribute(null, "installsource", data.getInstallSource());
-            serializer.attribute(null, "userid", "{" + getDeviceID() + "}");
-            serializer.attribute(null, "dedup", "uid");
+            if (ChromeFeatureList.isEnabled(ChromeFeatureList.ANONYMOUS_UPDATE_CHECKS)) {
+                serializer.attribute(null, "dedup", "cr");
+            } else {
+                serializer.attribute(null, "userid", "{" + getDeviceID() + "}");
+                serializer.attribute(null, "dedup", "uid");
+            }
 
             // Set up <os platform="android"... />
             serializer.startTag(null, "os");
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
index bcb6418..263e9a4e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/share/LensUtils.java
@@ -8,14 +8,11 @@
 import android.os.Build;
 import android.text.TextUtils;
 
-import androidx.annotation.VisibleForTesting;
-
 import org.chromium.chrome.browser.IntentHandler;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.gsa.GSAState;
 import org.chromium.chrome.browser.lens.LensController;
 import org.chromium.components.externalauth.ExternalAuthUtils;
-import org.chromium.url.GURL;
 
 /**
  * This class provides utilities for intenting into Google Lens.
@@ -23,30 +20,17 @@
 // TODO(crbug/1157496): Consolidate param-checks into a single function.
 public class LensUtils {
     private static final String MIN_AGSA_VERSION_FEATURE_PARAM_NAME = "minAgsaVersionName";
-    private static final String MIN_AGSA_VERSION_SHOPPING_FEATURE_PARAM_NAME =
-            "minAgsaVersionNameForShopping";
-    private static final String LENS_SHOPPING_ALLOWLIST_ENTRIES_FEATURE_PARAM_NAME =
-            "allowlistEntries";
-    private static final String LENS_SHOPPING_URL_PATTERNS_FEATURE_PARAM_NAME =
-            "shoppingUrlPatterns";
     private static final String LOG_UKM_PARAM_NAME = "logUkm";
     private static final String ENABLE_ON_TABLET_PARAM_NAME = "enableContextMenuSearchOnTablet";
     private static final String DISABLE_ON_INCOGNITO_PARAM_NAME = "disableOnIncognito";
     private static final String ORDER_SHARE_IMAGE_BEFORE_LENS_PARAM_NAME =
             "orderShareImageBeforeLens";
     private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE = "10.65";
-    private static final String MIN_AGSA_VERSION_NAME_FOR_LENS_CHROME_SHOPPING_INTENT = "11.16";
-    private static final int LENS_INTENT_TYPE_LENS_CHROME_SHOPPING = 18;
-    private static final String LENS_DEFAULT_SHOPPING_URL_PATTERNS =
-            "^https://www.google.com/shopping/.*|^https://www.google.com/.*tbm=shop.*";
 
     /**
      * See function for details.
      */
     private static boolean sFakePassableLensEnvironmentForTesting;
-    private static boolean sFakeImageUrlInShoppingAllowlistForTesting;
-    private static String sFakeInstalledAgsaVersion;
-    private static String sFakeVariationsForTesting;
 
     /*
      * If true, short-circuit the version name intent check to always return a high enough version.
@@ -58,31 +42,6 @@
         sFakePassableLensEnvironmentForTesting = shouldFake;
     }
 
-    @VisibleForTesting
-    public static void setFakeImageUrlInShoppingAllowlistForTesting(final boolean shouldFake) {
-        sFakeImageUrlInShoppingAllowlistForTesting = shouldFake;
-    }
-
-    /**
-     * Sets a fake installed agsa version name. Used by test cases to set versions below and above
-     * minimum required agsa versions.
-     */
-    @VisibleForTesting
-    public static void setFakeInstalledAgsaVersion(final String fakeAgsaVersionName) {
-        sFakeInstalledAgsaVersion = fakeAgsaVersionName;
-    }
-
-    /*
-     * If set, short-circuit the JNI call to retrieve the variation IDs. Used by
-     * test cases.
-     *
-     * @param fakeIdString Whether to fake the version check.
-     */
-    @VisibleForTesting
-    static void setFakeVariationsForTesting(final String fakeVariations) {
-        sFakeVariationsForTesting = fakeVariations;
-    }
-
     /**
      * Resolve the activity to verify that lens is ready to accept an intent and
      * also retrieve the version name.
@@ -93,12 +52,6 @@
      */
     public static String getLensActivityVersionNameIfAvailable(final Context context) {
         if (Boolean.TRUE.equals(sFakePassableLensEnvironmentForTesting)) {
-            // Returns the minimum version which will meet the bar and allow future AGSA
-            // version
-            // checks to succeed.
-            if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS)) {
-                return MIN_AGSA_VERSION_NAME_FOR_LENS_CHROME_SHOPPING_INTENT;
-            }
             return MIN_AGSA_VERSION_NAME_FOR_LENS_POSTCAPTURE;
         } else {
             if (context == null) {
@@ -140,32 +93,6 @@
     }
 
     /**
-     * Gets the minimum AGSA version required to support the Lens shopping context menu
-     * integration on this device. Takes the value from a server provided value if a
-     * field trial is active but otherwise will take the value from a client side
-     * default (unless the lens feature is not enabled at all, in which case return
-     * an empty string).
-     *
-     * @return The minimum version name string or an empty string if not available.
-     */
-    public static String getMinimumAgsaVersionForLensShoppingSupport() {
-        // Shopping feature AGSA version takes priority over Search with Google Lens
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS)) {
-            final String serverProvidedMinAgsaVersion =
-                    ChromeFeatureList.getFieldTrialParamByFeature(
-                            ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS,
-                            MIN_AGSA_VERSION_SHOPPING_FEATURE_PARAM_NAME);
-            if (TextUtils.isEmpty(serverProvidedMinAgsaVersion)) {
-                // Falls into this block if the user enabled the feature using chrome://flags
-                // and the param was not set by the server.
-                return MIN_AGSA_VERSION_NAME_FOR_LENS_CHROME_SHOPPING_INTENT;
-            }
-            return serverProvidedMinAgsaVersion;
-        }
-        return "";
-    }
-
-    /**
      * Checks whether the device is below Android O. We restrict to these versions
      * to limit to OS"s where image processing vulnerabilities can be retroactively
      * fixed if they are discovered in the future.
@@ -234,27 +161,6 @@
                 false);
     }
 
-    public static boolean isGoogleLensShoppingFeatureEnabled(boolean isIncognito) {
-        if (!ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS)) {
-            return false;
-        }
-
-        // Dont enable both the chip and the shopping menu item.
-        if (ChromeFeatureList.isEnabled(ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP)) {
-            return false;
-        }
-
-        // Disable on Incognito.
-        if (isIncognito
-                && ChromeFeatureList.getFieldTrialParamByFeatureAsBoolean(
-                        ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS,
-                        DISABLE_ON_INCOGNITO_PARAM_NAME, true)) {
-            return false;
-        }
-
-        return true;
-    }
-
     /**
      * Adjust chip ordering slightly. The image chip feature changes the context menu height
      * which can result  in the final image menu items being hidden in certain contexts.
@@ -266,56 +172,6 @@
                 ORDER_SHARE_IMAGE_BEFORE_LENS_PARAM_NAME, false);
     }
 
-    /**
-     * Gets the list of Allowlisted Entries as String. Format is a comma separated
-     * list of Entry names (as strings).
-     */
-    public static String getAllowlistEntries() {
-        return ChromeFeatureList.getFieldTrialParamByFeature(
-                ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST,
-                LENS_SHOPPING_ALLOWLIST_ENTRIES_FEATURE_PARAM_NAME);
-    }
-
-    /**
-     * Gets the list of shopping url patterns(regex) as String. Format is a "||" separated
-     * list of regex strings.
-     */
-    public static String getShoppingUrlPatterns() {
-        return ChromeFeatureList.getFieldTrialParamByFeature(
-                ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST,
-                LENS_SHOPPING_URL_PATTERNS_FEATURE_PARAM_NAME);
-    }
-
-    /**
-     * Check if the Lens shopping allowlist feature is enabled.
-     * The shopping allowlist is used to determine whether the image is shoppable.
-     * @return true if the shopping allowlist feature is enabled.
-     */
-    public static boolean isShoppingAllowlistEnabled() {
-        return ChromeFeatureList.isEnabled(
-                ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST);
-    }
-
-    /**
-     * Check if the page uri to determine whether the image is shoppable.
-     * @return true if the image is shoppable.
-     */
-    public static boolean isInShoppingAllowlist(final GURL url) {
-        if (sFakeImageUrlInShoppingAllowlistForTesting) {
-            return true;
-        }
-
-        if (!isShoppingAllowlistEnabled()) {
-            return false;
-        }
-
-        if (GURL.isEmptyOrInvalid(url)) {
-            return false;
-        }
-
-        return hasShoppingUrlPattern(url) || isInDomainAllowList(url);
-    }
-
     public static boolean shouldLogUkmForLensContextMenuFeatures() {
         return shouldLogUkmByFeature(ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS)
                 || shouldLogUkmByFeature(ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS)
@@ -336,48 +192,4 @@
         }
         return false;
     }
-
-    /**
-     * @return the Lens shopping intent type integer.
-     */
-    public static int getLensShoppingIntentType() {
-        return LENS_INTENT_TYPE_LENS_CHROME_SHOPPING;
-    }
-
-    /**
-     * Check if the the intent type is Lens shopping intent type.
-     * @return true if the intent type is shopping.
-     */
-    public static boolean isLensShoppingIntentType(int intentType) {
-        return intentType == getLensShoppingIntentType();
-    }
-
-    /**
-     * Check if the uri matches any shopping url patterns.
-     */
-    private static boolean hasShoppingUrlPattern(final GURL url) {
-        String shoppingUrlPatterns = getShoppingUrlPatterns();
-        if (shoppingUrlPatterns == null || shoppingUrlPatterns.isEmpty()) {
-            // Fallback to default shopping url patterns.
-            shoppingUrlPatterns = LENS_DEFAULT_SHOPPING_URL_PATTERNS;
-        }
-
-        return url.getSpec().matches(shoppingUrlPatterns);
-    }
-
-    /**
-     * Check if the uri domain is in the Lens shopping domain Allowlist.
-     */
-    private static boolean isInDomainAllowList(final GURL url) {
-        final String allowlistEntries = getAllowlistEntries();
-        final String[] allowlist = allowlistEntries.split(",");
-
-        for (final String allowlistEntry : allowlist) {
-            if (allowlistEntry.length() > 0 && url.getSpec().contains(allowlistEntry)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
 }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
index b8cab80..25caa16 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuTest.java
@@ -193,31 +193,6 @@
     @Test
     @MediumTest
     @Feature({"Browser"})
-    @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST})
-    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS})
-    public void testLensShoppingAllowlistWithLensFeaturesDisabled() throws Throwable {
-        Tab tab = mDownloadTestRule.getActivity().getActivityTab();
-
-        LensUtils.setFakePassableLensEnvironmentForTesting(true);
-        LensUtils.setFakeImageUrlInShoppingAllowlistForTesting(true);
-        ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true);
-        hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION);
-
-        ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(),
-                mDownloadTestRule.getActivity(), tab, "testImage",
-                R.id.contextmenu_search_by_image);
-
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramTotalCountForTesting(
-                        "ContextMenu.SelectedOptionAndroid.Image"));
-        Assert.assertEquals(1,
-                RecordHistogram.getHistogramTotalCountForTesting(
-                        "ContextMenu.SelectedOptionAndroid.Image.ShoppingDomain"));
-    }
-
-    @Test
-    @MediumTest
-    @Feature({"Browser"})
     public void testLongPressOnImage() throws TimeoutException {
         checkOpenImageInNewTab("testImage", "/chrome/test/data/android/contextmenu/test_image.png");
     }
@@ -669,54 +644,6 @@
     @Test
     @SmallTest
     @Feature({"Browser", "ContextMenu"})
-    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP})
-    @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST,
-            ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void
-    testContextMenuLensEnabledShopImageWithGoogleLens() throws TimeoutException {
-        LensUtils.setFakePassableLensEnvironmentForTesting(true);
-        LensUtils.setFakeImageUrlInShoppingAllowlistForTesting(true);
-        Tab tab = mDownloadTestRule.getActivity().getActivityTab();
-        ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage");
-
-        Integer[] expectedItems = {R.id.contextmenu_save_image,
-                R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image,
-                R.id.contextmenu_shop_image_with_google_lens, R.id.contextmenu_copy_image};
-        Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab};
-        expectedItems =
-                addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems);
-        assertMenuItemsAreEqual(menu, expectedItems);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Browser", "ContextMenu"})
-    @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS})
-    @CommandLineFlags.
-    Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST
-                    + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:shoppingUrlPatterns/^shopping-site.*"})
-    @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
-    public void
-    testContextMenuLensDisableShopWithGoogleLensForShoppingUrl() throws TimeoutException {
-        LensUtils.setFakePassableLensEnvironmentForTesting(true);
-        Tab tab = mDownloadTestRule.getActivity().getActivityTab();
-        ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage");
-
-        Integer[] expectedItems = {R.id.contextmenu_save_image,
-                R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_with_google_lens,
-                R.id.contextmenu_share_image, R.id.contextmenu_copy_image};
-        Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab};
-        expectedItems =
-                addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems);
-        assertMenuItemsAreEqual(menu, expectedItems);
-    }
-
-    @Test
-    @SmallTest
-    @Feature({"Browser", "ContextMenu"})
     @Policies.Add({ @Policies.Item(key = "DefaultSearchProviderEnabled", string = "false") })
     public void testContextMenuRetrievesImageOptions_NoDefaultSearchEngine()
             throws TimeoutException {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
index b9ecdbef..daa94d3f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/OmahaBaseTest.java
@@ -18,8 +18,10 @@
 import org.junit.runner.RunWith;
 
 import org.chromium.base.ApiCompatibilityUtils;
+import org.chromium.base.FeatureList;
 import org.chromium.base.test.util.AdvancedMockContext;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.omaha.MockRequestGenerator;
 import org.chromium.chrome.test.omaha.MockRequestGenerator.DeviceType;
@@ -188,10 +190,14 @@
         Context targetContext = InstrumentationRegistry.getTargetContext();
         OmahaBase.setIsDisabledForTesting(false);
         mContext = new AdvancedMockContext(targetContext);
+        FeatureList.TestValues overrides = new FeatureList.TestValues();
+        overrides.addFeatureFlagOverride(ChromeFeatureList.ANONYMOUS_UPDATE_CHECKS, true);
+        FeatureList.setTestValues(overrides);
     }
 
     @After
     public void tearDown() {
+        FeatureList.setTestValues(null);
         OmahaBase.setIsDisabledForTesting(true);
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
index 8abf7fc..274405f 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omaha/RequestGeneratorTest.java
@@ -14,13 +14,17 @@
 
 import androidx.test.filters.SmallTest;
 
+import org.junit.After;
 import org.junit.Assert;
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.FeatureList;
 import org.chromium.base.test.util.AdvancedMockContext;
 import org.chromium.base.test.util.Feature;
+import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
 import org.chromium.chrome.browser.uid.SettingsSecureBasedIdentificationGenerator;
 import org.chromium.chrome.browser.uid.UniqueIdentificationGenerator;
@@ -42,6 +46,18 @@
     @Rule
     public final AccountManagerTestRule mAccountManagerTestRule = new AccountManagerTestRule();
 
+    @Before
+    public void setUp() {
+        FeatureList.TestValues overrides = new FeatureList.TestValues();
+        overrides.addFeatureFlagOverride(ChromeFeatureList.ANONYMOUS_UPDATE_CHECKS, true);
+        FeatureList.setTestValues(overrides);
+    }
+
+    @After
+    public void tearDown() {
+        FeatureList.setTestValues(null);
+    }
+
     @Test
     @SmallTest
     @Feature({"Omaha"})
@@ -179,8 +195,6 @@
                     checkForTag(xml, "updatecheck"));
         }
 
-        checkForAttributeAndValue(xml, "request", "userid", "{" + generator.getDeviceID() + "}");
-
         return generator;
     }
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java
index 93a270479..1991dcd 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/share/LensUtilsTest.java
@@ -29,10 +29,7 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
-import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
-import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.url.GURL;
 
 /**
  * Tests of {@link LensUtils}.
@@ -122,119 +119,6 @@
     }
 
     /**
-     * Test {@link LensUtils#isGoogleLensFeatureEnabled()} method when disable incognito param is
-     * unset and user is incognito.
-     */
-    @DisableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP})
-    @EnableFeatures({ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS})
-    @Test
-    @SmallTest
-    public void isGoogleLensShoppingFeatureEnabled_incognitoParamUnsetIncognitoUser() {
-        Assert.assertFalse("Feature incorrectly enabled when incognito param is not set",
-                isGoogleLensShoppingFeatureEnabledOnUiThread(true));
-    }
-
-    /**
-     * Test {@link LensUtils#isGoogleLensShoppingFeatureEnabled()} method when incognito users are
-     * enabled and user is incognito.
-     */
-    @DisableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP})
-    @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS
-                    + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:disableOnIncognito/false"})
-    @Test
-    @SmallTest
-    public void
-    isGoogleLensShoppingFeatureEnabled_incognitoEnabledIncognitoUser() {
-        Assert.assertTrue("Feature incorrectly disabled when incognito param is not set",
-                isGoogleLensShoppingFeatureEnabledOnUiThread(true));
-    }
-
-    /**
-     * Test {@link LensUtils#isGoogleLensFeatureEnabled()} method when incognito users are disabled
-     * and user is incognito.
-     */
-    @DisableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP})
-    @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS
-                    + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:disableOnIncognito/true"})
-    @Test
-    @SmallTest
-    public void
-    isGoogleLensShoppingFeatureEnabled_incognitoDisabledIncognitoUser() {
-        Assert.assertFalse("Feature incorrectly not disabled when incognito param was set",
-                isGoogleLensShoppingFeatureEnabledOnUiThread(true));
-    }
-
-    /**
-     * Test {@link LensUtils#isGoogleLensFeatureEnabled()} method when incognito users are disabled
-     * and user is not incognito.
-     */
-    @DisableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP})
-    @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS
-                    + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:disableOnIncognito/true"})
-    @Test
-    @SmallTest
-    public void
-    isGoogleLensShoppingFeatureEnabled_incognitoDisabledStandardUser() {
-        Assert.assertTrue("Feature incorrectly disabled when user was not incognito",
-                isGoogleLensShoppingFeatureEnabledOnUiThread(false));
-    }
-
-    /**
-     * Test {@link LensUtils#isGoogleLensFeatureEnabled()} method when shopping chip is enabled.
-     */
-    @CommandLineFlags.Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS
-                    + "<FakeStudyName," + ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP
-                    + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:disableOnIncognito/true"})
-    @Test
-    @SmallTest
-    public void
-    isGoogleLensShoppingFeatureEnabled_shoppingChipEnabled() {
-        Assert.assertFalse("Feature incorrectly enabled when shopping chip was enabled",
-                isGoogleLensShoppingFeatureEnabledOnUiThread(false));
-    }
-
-    /**
-     * Test {@link LensUtils#isInShoppingAllowlist(url)} method for url in domain allowlist.
-     */
-    @CommandLineFlags.
-    Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST
-                    + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:allowlistEntries/shopping-site-2"})
-    @Test
-    @SmallTest
-    public void
-    isInShoppingAllowlistWithDomainAllowlistTest() {
-        final GURL pageUrl = new GURL("https://shopping-site-2.com/product_1");
-        assertTrue(isInShoppingAllowlistOnUiThread(pageUrl));
-    }
-
-    /**
-     * Test {@link LensUtils#isInShoppingAllowlist(url)} method for url with shopping url patterns.
-     */
-    @CommandLineFlags.
-    Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST
-                    + "<FakeStudyName",
-            "force-fieldtrials=FakeStudyName/Enabled",
-            "force-fieldtrial-params=FakeStudyName.Enabled:shoppingUrlPatterns/"
-                    + "^https...shopping-site.*"})
-    @Test
-    @SmallTest
-    public void
-    isInShoppingAllowlistWithShoppingUrlPatternsTest() {
-        final GURL pageUrl = new GURL("https://shopping-site-2.com/product_1");
-        assertTrue(isInShoppingAllowlistOnUiThread(pageUrl));
-    }
-
-    /**
      * Test {@link LensUtils#shouldLogUkmByFeature(featureName)} method for enable UKM logging for
      * Lens chip integration.
      */
@@ -331,36 +215,11 @@
                 isGoogleLensFeatureEnabledOnTabletOnUiThread());
     }
 
-    /**
-     * Test {@link LensUtils#isInShoppingAllowlist(url)} method for url with default shopping url
-     * patterns.
-     */
-    @Test
-    @SmallTest
-    @EnableFeatures({ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST})
-    public void isInShoppingAllowlistWithDefaultShoppingUrlPatternTest() {
-        final GURL googleShoppingItemUrl = new GURL("https://www.google.com/shopping/product_1");
-        final GURL googleShoppingPageUrl =
-                new GURL("https://www.google.com/search?=8893t5/tbm=shop/dress");
-        assertTrue(isInShoppingAllowlistOnUiThread(googleShoppingPageUrl));
-        assertTrue(isInShoppingAllowlistOnUiThread(googleShoppingItemUrl));
-    }
-
-    private boolean isInShoppingAllowlistOnUiThread(GURL imageUri) {
-        return TestThreadUtils.runOnUiThreadBlockingNoException(
-                () -> LensUtils.isInShoppingAllowlist(imageUri));
-    }
-
     private boolean isGoogleLensFeatureEnabledOnUiThread(boolean isIncognito) {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> LensUtils.isGoogleLensFeatureEnabled(isIncognito));
     }
 
-    private boolean isGoogleLensShoppingFeatureEnabledOnUiThread(boolean isIncognito) {
-        return TestThreadUtils.runOnUiThreadBlockingNoException(
-                () -> LensUtils.isGoogleLensShoppingFeatureEnabled(isIncognito));
-    }
-
     private boolean shouldLogUkmOnUiThread(String featureName) {
         return TestThreadUtils.runOnUiThreadBlockingNoException(
                 () -> LensUtils.shouldLogUkmByFeature(featureName));
diff --git a/chrome/android/profiles/newest.txt b/chrome/android/profiles/newest.txt
index c6dcbd27..b58b2756 100644
--- a/chrome/android/profiles/newest.txt
+++ b/chrome/android/profiles/newest.txt
@@ -1 +1 @@
-chromeos-chrome-amd64-99.0.4768.0_rc-r1-merged.afdo.bz2
+chromeos-chrome-amd64-99.0.4777.0_rc-r1-merged.afdo.bz2
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index 428798af..e0b9da01 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3379,6 +3379,7 @@
       "//chrome/browser/android/messages:jni_headers",
       "//chrome/browser/android/metrics:jni_headers",
       "//chrome/browser/attribution_reporting/android:jni_headers",
+      "//chrome/browser/attribution_reporting/android/internal",
       "//chrome/browser/banners/android:jni_headers",
       "//chrome/browser/commerce:feature_list",
       "//chrome/browser/commerce/merchant_viewer:merchant_signal_db",
diff --git a/chrome/browser/apps/app_service/intent_util.cc b/chrome/browser/apps/app_service/intent_util.cc
index 611c4e6e..2ebf2553 100644
--- a/chrome/browser/apps/app_service/intent_util.cc
+++ b/chrome/browser/apps/app_service/intent_util.cc
@@ -781,6 +781,7 @@
       if (file_system_url.is_valid()) {
         auto crosapi_file = crosapi::mojom::IntentFile::New();
         crosapi_file->file_path = file_system_url.path();
+        crosapi_file->mime_type = file->mime_type;
         crosapi_files.push_back(std::move(crosapi_file));
       }
     }
@@ -826,6 +827,7 @@
       intent_file->file_name =
           base::SafeBaseName::Create(file->file_path.BaseName());
 #endif
+      intent_file->mime_type = file->mime_type;
 
       intent_files.push_back(std::move(intent_file));
     }
diff --git a/chrome/browser/apps/app_service/intent_util_unittest.cc b/chrome/browser/apps/app_service/intent_util_unittest.cc
index 9e4057c..c2b4837 100644
--- a/chrome/browser/apps/app_service/intent_util_unittest.cc
+++ b/chrome/browser/apps/app_service/intent_util_unittest.cc
@@ -704,12 +704,14 @@
 TEST_F(IntentUtilsFileTest, AppServiceIntentToCrosapi) {
   auto app_service_intent = apps::mojom::Intent::New();
   app_service_intent->action = "action";
-  app_service_intent->mime_type = "mime_type";
+  app_service_intent->mime_type = "*/*";
   const std::string path = "Documents/foo.txt";
+  const std::string mime_type = "text/plain";
   auto url = ToGURL(base::FilePath(storage::kTestDir), path);
   app_service_intent->files = std::vector<apps::mojom::IntentFilePtr>{};
   auto file = apps::mojom::IntentFile::New();
   file->url = url;
+  file->mime_type = mime_type;
   app_service_intent->files->push_back(std::move(file));
   auto crosapi_intent = apps_util::ConvertAppServiceToCrosapiIntent(
       app_service_intent, GetProfile());
@@ -718,6 +720,7 @@
   ASSERT_TRUE(crosapi_intent->files.has_value());
   ASSERT_EQ(crosapi_intent->files.value().size(), 1U);
   EXPECT_EQ(crosapi_intent->files.value()[0]->file_path, base::FilePath(path));
+  EXPECT_EQ(crosapi_intent->files.value()[0]->mime_type, mime_type);
 }
 
 TEST_F(IntentUtilsFileTest, CrosapiIntentToAppService) {
diff --git a/chrome/browser/apps/app_service/launch_utils_unittest.cc b/chrome/browser/apps/app_service/launch_utils_unittest.cc
index 9481ecae..009d46d 100644
--- a/chrome/browser/apps/app_service/launch_utils_unittest.cc
+++ b/chrome/browser/apps/app_service/launch_utils_unittest.cc
@@ -291,6 +291,7 @@
   constexpr char kIntentMimeType[] = "image/*";
   constexpr char kShareText[] = "Message";
   constexpr char kFilePath[] = "/tmp/picture.png";
+  constexpr char kFileMimeType[] = "image/png";
   constexpr char kBaseName[] = "picture.png";
 
   crosapi::mojom::LaunchParamsPtr crosapi_params =
@@ -308,6 +309,7 @@
     std::vector<crosapi::mojom::IntentFilePtr> crosapi_files;
     auto crosapi_file = crosapi::mojom::IntentFile::New();
     crosapi_file->file_path = base::FilePath(kFilePath);
+    crosapi_file->mime_type = kFileMimeType;
     crosapi_files.push_back(std::move(crosapi_file));
     crosapi_params->intent->files = std::move(crosapi_files);
   }
@@ -331,6 +333,7 @@
   EXPECT_EQ(converted_params.intent->files->size(), 1U);
   EXPECT_EQ((*converted_params.intent->files)[0]->file_name,
             base::SafeBaseName::Create(kBaseName));
+  EXPECT_EQ((*converted_params.intent->files)[0]->mime_type, kFileMimeType);
 }
 #endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
 
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc
index 258122e..165b348 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.cc
@@ -6,6 +6,7 @@
 
 #include "ash/shell.h"
 #include "base/metrics/histogram_macros.h"
+#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
 #include "chrome/browser/apps/app_service/web_contents_app_id_utils.h"
 #include "chrome/browser/ui/browser.h"
 #include "chrome/browser/ui/browser_finder.h"
@@ -14,6 +15,8 @@
 #include "components/services/app_service/public/cpp/types_util.h"
 #include "content/public/browser/web_contents.h"
 #include "extensions/common/constants.h"
+#include "services/metrics/public/cpp/ukm_builders.h"
+#include "services/metrics/public/cpp/ukm_recorder.h"
 #include "ui/aura/window.h"
 #include "ui/events/event_constants.h"
 #include "ui/events/types/event_type.h"
@@ -58,19 +61,17 @@
 
 AppPlatformInputMetrics::AppPlatformInputMetrics(
     Profile* profile,
+    apps::AppRegistryCache& app_registry_cache,
     InstanceRegistry& instance_registry)
     : profile_(profile) {
   InstanceRegistry::Observer::Observe(&instance_registry);
-  // Check whether ash::Shell exists before calling it to avoid crash, and keep
-  // consistent implementation with the destructor function.
+  AppRegistryCache::Observer::Observe(&app_registry_cache);
   if (ash::Shell::HasInstance()) {
     ash::Shell::Get()->AddPreTargetHandler(this);
   }
 }
 
 AppPlatformInputMetrics::~AppPlatformInputMetrics() {
-  // ash::Shell might be destroyed before AppPlatformInputMetrics's destroy, so
-  // check whether ash::Shell exists before calling it to avoid crash.
   if (ash::Shell::HasInstance()) {
     ash::Shell::Get()->RemovePreTargetHandler(this);
   }
@@ -96,6 +97,20 @@
   }
 }
 
+void AppPlatformInputMetrics::OnFiveMinutes() {
+  for (const auto& event_counts : app_id_to_event_count_per_five_minutes_) {
+    ukm::SourceId source_id = GetSourceId(event_counts.first);
+    if (source_id == ukm::kInvalidSourceId) {
+      continue;
+    }
+
+    // `event_counts.second` is the map from InputEventSource to the event
+    // counts.
+    RecordInputEventsUkm(source_id, event_counts.second);
+  }
+  app_id_to_event_count_per_five_minutes_.clear();
+}
+
 void AppPlatformInputMetrics::OnInstanceUpdate(const InstanceUpdate& update) {
   if (!update.StateChanged()) {
     return;
@@ -132,6 +147,29 @@
   InstanceRegistry::Observer::Observe(nullptr);
 }
 
+void AppPlatformInputMetrics::OnAppRegistryCacheWillBeDestroyed(
+    AppRegistryCache* cache) {
+  AppRegistryCache::Observer::Observe(nullptr);
+}
+
+void AppPlatformInputMetrics::OnAppUpdate(const AppUpdate& update) {
+  if (!update.ReadinessChanged() ||
+      apps_util::IsInstalled(update.Readiness())) {
+    return;
+  }
+
+  auto it = app_id_to_source_id_.find(update.AppId());
+  if (it == app_id_to_source_id_.end()) {
+    return;
+  }
+
+  // Remove the source id when the app is removed. The source id will be added
+  // when record the UKM, so we don't need to add the source id here when the
+  // app is installed.
+  AppPlatformMetrics::RemoveSourceId(it->second);
+  app_id_to_source_id_.erase(it);
+}
+
 void AppPlatformInputMetrics::RecordEventCount(InputEventSource event_source,
                                                ui::EventTarget* event_target) {
   views::Widget* target = views::Widget::GetTopLevelWidgetForNativeView(
@@ -154,4 +192,33 @@
                                            [it->second.app_type_name];
 }
 
+ukm::SourceId AppPlatformInputMetrics::GetSourceId(const std::string& app_id) {
+  auto it = app_id_to_source_id_.find(app_id);
+  if (it != app_id_to_source_id_.end()) {
+    return it->second;
+  }
+
+  auto source_id = AppPlatformMetrics::GetSourceId(profile_, app_id);
+  app_id_to_source_id_[app_id] = source_id;
+  return source_id;
+}
+
+void AppPlatformInputMetrics::RecordInputEventsUkm(
+    ukm::SourceId source_id,
+    const EventSourceToCounts& event_counts) {
+  for (const auto& counts : event_counts) {
+    InputEventSource event_source = counts.first;
+
+    // `counts.second` is the map from AppTypeName to the event count.
+    for (const auto& count : counts.second) {
+      ukm::builders::ChromeOSApp_InputEvent builder(source_id);
+      builder.SetAppType((int)count.first)
+          .SetAppInputEventSource((int)event_source)
+          .SetAppInputEventCount(count.second)
+          .SetUserDeviceMatrix(GetUserTypeByDeviceTypeMetrics())
+          .Record(ukm::UkmRecorder::Get());
+    }
+  }
+}
+
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h
index 7a0bee15..c17b3d0 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_input_metrics.h
@@ -10,6 +10,7 @@
 #include "base/containers/flat_map.h"
 #include "chrome/browser/apps/app_service/metrics/app_platform_metrics_utils.h"
 #include "chrome/browser/profiles/profile.h"
+#include "components/services/app_service/public/cpp/app_registry_cache.h"
 #include "components/services/app_service/public/cpp/instance_registry.h"
 #include "services/metrics/public/cpp/ukm_source_id.h"
 #include "ui/events/event_handler.h"
@@ -33,9 +34,11 @@
 
 // This class is used to record the input events for the app windows.
 class AppPlatformInputMetrics : public ui::EventHandler,
+                                public AppRegistryCache::Observer,
                                 public InstanceRegistry::Observer {
  public:
   AppPlatformInputMetrics(Profile* profile,
+                          AppRegistryCache& app_registry_cache,
                           InstanceRegistry& instance_registry);
 
   AppPlatformInputMetrics(const AppPlatformInputMetrics&) = delete;
@@ -48,6 +51,8 @@
   void OnKeyEvent(ui::KeyEvent* event) override;
   void OnTouchEvent(ui::TouchEvent* event) override;
 
+  void OnFiveMinutes();
+
  private:
   struct AppInfo {
     std::string app_id;
@@ -65,18 +70,31 @@
   void OnInstanceUpdate(const InstanceUpdate& update) override;
   void OnInstanceRegistryWillBeDestroyed(InstanceRegistry* cache) override;
 
+  // AppRegistryCache::Observer:
+  void OnAppRegistryCacheWillBeDestroyed(AppRegistryCache* cache) override;
+  void OnAppUpdate(const AppUpdate& update) override;
+
   void RecordEventCount(InputEventSource event_source,
                         ui::EventTarget* event_target);
 
-  ukm::SourceId GetSourceId(const std::string app_id);
+  ukm::SourceId GetSourceId(const std::string& app_id);
+
+  void RecordInputEventsUkm(ukm::SourceId source_id,
+                            const EventSourceToCounts& event_counts);
 
   Profile* profile_;
 
   // The map from the window to the app info.
   base::flat_map<aura::Window*, AppInfo> window_to_app_info_;
 
-  // Records the input event count for each app id in the past five minutes.
-  // Each app id might have multiple events. For web apps and Chrome apps, there
+  // The map from the app id to the source id to reuse the existing source id
+  // for the same app id based on the UKM guidelines. When the app is removed,
+  // the record for the app will be removed, and inform UKM service that the
+  // source_id is no longer used.
+  std::map<std::string, ukm::SourceId> app_id_to_source_id_;
+
+  // Records the input even count for each app id in the past five minutes. Each
+  // app id might have multiple events. For web apps and Chrome apps, there
   // might be different app type name, e.g. Chrome browser for apps opening in
   // a tab, or Web app for apps opening in a window. For example:
   // web_app_id1: {
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
index e0466e8..6978f57 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.cc
@@ -512,6 +512,11 @@
 }
 
 // static
+void AppPlatformMetrics::RemoveSourceId(ukm::SourceId source_id) {
+  ukm::AppSourceUrlRecorder::MarkSourceForDeletion(source_id);
+}
+
+// static
 std::string AppPlatformMetrics::GetAppsCountHistogramNameForTest(
     AppTypeName app_type_name) {
   return kAppsCountHistogramPrefix + GetAppTypeHistogramName(app_type_name);
@@ -617,7 +622,7 @@
       .SetLaunchSource((int)launch_source)
       .SetUserDeviceMatrix(GetUserTypeByDeviceTypeMetrics())
       .Record(ukm::UkmRecorder::Get());
-  ukm::AppSourceUrlRecorder::MarkSourceForDeletion(source_id);
+  RemoveSourceId(source_id);
 }
 
 void AppPlatformMetrics::RecordAppUninstallUkm(
@@ -638,7 +643,7 @@
       .SetUninstallSource((int)uninstall_source)
       .SetUserDeviceMatrix(user_type_by_device_type_)
       .Record(ukm::UkmRecorder::Get());
-  ukm::AppSourceUrlRecorder::MarkSourceForDeletion(source_id);
+  RemoveSourceId(source_id);
 }
 
 void AppPlatformMetrics::OnAppTypeInitialized(apps::mojom::AppType app_type) {
@@ -1078,7 +1083,7 @@
     }
     if (it.second.window_is_closed) {
       closed_instance_ids.push_back(it.first);
-      ukm::AppSourceUrlRecorder::MarkSourceForDeletion(source_id);
+      RemoveSourceId(source_id);
     } else {
       it.second.running_time = base::TimeDelta();
     }
@@ -1107,7 +1112,7 @@
       .SetInstallTime((int)install_time)
       .SetUserDeviceMatrix(user_type_by_device_type_)
       .Record(ukm::UkmRecorder::Get());
-  ukm::AppSourceUrlRecorder::MarkSourceForDeletion(source_id);
+  RemoveSourceId(source_id);
 }
 
 }  // namespace apps
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
index 92751d5..c757bf0 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics.h
@@ -94,6 +94,10 @@
   static ukm::SourceId GetSourceIdForCrostini(Profile* profile,
                                               const std::string& app_id);
 
+  // Informs UKM service that the source_id is no longer needed and can be
+  // deleted later.
+  static void RemoveSourceId(ukm::SourceId source_id);
+
   // UMA metrics name for installed apps count in Chrome OS.
   static std::string GetAppsCountHistogramNameForTest(
       AppTypeName app_type_name);
diff --git a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
index f170621..235d5c1 100644
--- a/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
+++ b/chrome/browser/apps/app_service/metrics/app_platform_metrics_service.cc
@@ -54,7 +54,7 @@
   app_platform_app_metrics_ = std::make_unique<apps::AppPlatformMetrics>(
       profile_, app_registry_cache, instance_registry);
   app_platform_input_metrics_ = std::make_unique<apps::AppPlatformInputMetrics>(
-      profile_, instance_registry);
+      profile_, app_registry_cache, instance_registry);
 
   day_id_ = profile_->GetPrefs()->GetInteger(kAppPlatformMetricsDayId);
   CheckForNewDay();
@@ -83,6 +83,7 @@
 
 void AppPlatformMetricsService::CheckForFiveMinutes() {
   app_platform_app_metrics_->OnFiveMinutes();
+  app_platform_input_metrics_->OnFiveMinutes();
 }
 
 }  // namespace apps
diff --git a/chrome/browser/ash/borealis/borealis_window_manager.cc b/chrome/browser/ash/borealis/borealis_window_manager.cc
index c45fcce..3e1dcde7 100644
--- a/chrome/browser/ash/borealis/borealis_window_manager.cc
+++ b/chrome/browser/ash/borealis/borealis_window_manager.cc
@@ -6,6 +6,9 @@
 
 #include <string>
 
+#include "ash/wm/window_state.h"
+#include "ash/wm/window_util.h"
+#include "base/base64.h"
 #include "base/containers/flat_map.h"
 #include "base/containers/flat_set.h"
 #include "base/logging.h"
@@ -33,6 +36,11 @@
 // the GuestOsRegistryService), so to identify them we prepend this.
 const char kBorealisAnonymousPrefix[] = "borealis_anon:";
 
+// Base64-encoded shell application id of borealis client when it is in full-
+// screen mode.
+const char kFullscreenClientShellId[] =
+    "b3JnLmNocm9taXVtLmJvcmVhbGlzLndtY2xhc3Muc3RlYW0=";
+
 DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kShelfAppIdKey, nullptr)
 
 // Returns an ID for this window (which is the app_id or startup_id, depending
@@ -111,6 +119,30 @@
   return base::StartsWith(window_id, kBorealisWindowPrefix);
 }
 
+// static
+bool BorealisWindowManager::ShouldNewWindowBeMinimized() {
+  aura::Window* active_window = ash::window_util::GetActiveWindow();
+  if (!active_window || !IsBorealisWindow(active_window))
+    return false;
+
+  auto* window_state = ash::WindowState::Get(active_window);
+  if (!window_state || !window_state->IsFullscreen())
+    return false;
+
+  const std::string* active_window_id = GetWindowId(active_window);
+  if (!active_window_id)
+    return false;
+
+  std::string fullscreen_client_id;
+  if (!base::Base64Decode(kFullscreenClientShellId, &fullscreen_client_id))
+    return false;
+
+  if (*active_window_id == fullscreen_client_id)
+    return false;
+
+  return true;
+}
+
 BorealisWindowManager::BorealisWindowManager(Profile* profile)
     : profile_(profile), instance_registry_observation_(this) {}
 
diff --git a/chrome/browser/ash/borealis/borealis_window_manager.h b/chrome/browser/ash/borealis/borealis_window_manager.h
index d76775f..1f911d3 100644
--- a/chrome/browser/ash/borealis/borealis_window_manager.h
+++ b/chrome/browser/ash/borealis/borealis_window_manager.h
@@ -35,6 +35,10 @@
   // Returns true if this window's ID belongs to a borealis VM.
   static bool IsBorealisWindowId(const std::string& window_id);
 
+  // Determines if a newly created window should be minimized on creation.
+  // TODO(b/210569001): this is intended to be a temporary solution.
+  static bool ShouldNewWindowBeMinimized();
+
   // An observer for tracking the creation and deletion of anonymous windows.
   class AnonymousAppObserver : public base::CheckedObserver {
    public:
diff --git a/chrome/browser/ash/crosapi/browser_manager.cc b/chrome/browser/ash/crosapi/browser_manager.cc
index d06b5711..fba40276 100644
--- a/chrome/browser/ash/crosapi/browser_manager.cc
+++ b/chrome/browser/ash/crosapi/browser_manager.cc
@@ -43,6 +43,7 @@
 #include "chrome/browser/ash/crosapi/browser_data_migrator.h"
 #include "chrome/browser/ash/crosapi/browser_loader.h"
 #include "chrome/browser/ash/crosapi/browser_service_host_ash.h"
+#include "chrome/browser/ash/crosapi/browser_util.h"
 #include "chrome/browser/ash/crosapi/crosapi_ash.h"
 #include "chrome/browser/ash/crosapi/crosapi_manager.h"
 #include "chrome/browser/ash/crosapi/environment_provider.h"
@@ -59,6 +60,7 @@
 #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/crosapi/cpp/lacros_startup_state.h"
 #include "chromeos/startup/startup_switches.h"
 #include "components/policy/proto/device_management_backend.pb.h"
 #include "components/prefs/pref_service.h"
@@ -503,8 +505,17 @@
 
   PrepareLacrosPolicies();
 
+  const bool is_enabled = browser_util::IsLacrosEnabled();
+
+  // As a switch between Ash and Lacros mode requires an Ash restart plus
+  // profile migration, the state will not change while the system is up.
+  // At this point we are starting Lacros for the first time and with that the
+  // operation mode is 'locked in'.
+  crosapi::lacros_startup_state::SetLacrosStartupState(
+      is_enabled, browser_util::IsLacrosPrimaryBrowser());
+
   // Must be checked after user session start because it depends on user type.
-  if (browser_util::IsLacrosEnabled()) {
+  if (is_enabled) {
     component_update_observation_.Observe(component_update_service_);
     SetState(State::MOUNTING);
     browser_loader_->Load(base::BindOnce(&BrowserManager::OnLoadComplete,
diff --git a/chrome/browser/ash/crostini/crostini_terminal.cc b/chrome/browser/ash/crostini/crostini_terminal.cc
index ba32afc9..be3437ac 100644
--- a/chrome/browser/ash/crostini/crostini_terminal.cc
+++ b/chrome/browser/ash/crostini/crostini_terminal.cc
@@ -159,10 +159,10 @@
   if (images.size() >= resource_ids.size()) {
     return std::move(callback).Run(std::move(images));
   }
+  auto resource_id = resource_ids[images.size()];
   apps::LoadIconFromResource(
-      apps::IconType::kStandard, apps::kAppShortcutIconSizeDip,
-      resource_ids[images.size()], /*placeholder=*/false,
-      apps::IconEffects::kNone,
+      apps::IconType::kStandard, apps::kAppShortcutIconSizeDip, resource_id,
+      /*placeholder=*/false, apps::IconEffects::kNone,
       base::BindOnce(
           [](std::vector<int> resource_ids, std::vector<gfx::ImageSkia> images,
              base::OnceCallback<void(std::vector<gfx::ImageSkia>)> callback,
diff --git a/chrome/browser/ash/guest_os/guest_os_registry_service.cc b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
index aa9df56..445df18 100644
--- a/chrome/browser/ash/guest_os/guest_os_registry_service.cc
+++ b/chrome/browser/ash/guest_os/guest_os_registry_service.cc
@@ -75,8 +75,10 @@
     std::string locale_with_dashes(locale);
     std::replace(locale_with_dashes.begin(), locale_with_dashes.end(), '_',
                  '-');
-    if (!locale.empty() && !l10n_util::IsValidLocaleSyntax(locale_with_dashes))
+    if (!locale.empty() &&
+        !l10n_util::IsValidLocaleSyntax(locale_with_dashes)) {
       continue;
+    }
 
     result.SetKey(locale, base::Value(entry.value()));
   }
@@ -86,19 +88,22 @@
 std::set<std::string> ListToStringSet(const base::Value* list,
                                       bool to_lower_ascii = false) {
   std::set<std::string> result;
-  if (!list)
+  if (!list) {
     return result;
-  for (const base::Value& value : list->GetList())
+  }
+  for (const base::Value& value : list->GetList()) {
     result.insert(to_lower_ascii ? base::ToLowerASCII(value.GetString())
                                  : value.GetString());
+  }
   return result;
 }
 
 base::Value ProtoToList(
     const google::protobuf::RepeatedPtrField<std::string>& strings) {
   base::Value result(base::Value::Type::LIST);
-  for (const std::string& string : strings)
+  for (const std::string& string : strings) {
     result.Append(string);
+  }
   return result;
 }
 
@@ -111,8 +116,10 @@
     std::string locale_with_dashes(locale);
     std::replace(locale_with_dashes.begin(), locale_with_dashes.end(), '_',
                  '-');
-    if (!locale.empty() && !l10n_util::IsValidLocaleSyntax(locale_with_dashes))
+    if (!locale.empty() &&
+        !l10n_util::IsValidLocaleSyntax(locale_with_dashes)) {
       continue;
+    }
     result.SetKey(locale, ProtoToList(strings_with_locale.value()));
   }
   return result;
@@ -157,18 +164,6 @@
                            base::Value(app.package_id()));
 }
 
-// This is the companion to GuestOsRegistryService::SetCurrentTime().
-base::Time GetTime(const base::Value& pref, const char* key) {
-  if (!pref.is_dict())
-    return base::Time();
-
-  const base::Value* value = pref.FindKeyOfType(key, base::Value::Type::STRING);
-  int64_t time;
-  if (!value || !base::StringToInt64(value->GetString(), &time))
-    return base::Time();
-  return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(time));
-}
-
 bool EqualsExcludingTimestamps(const base::Value& left,
                                const base::Value& right) {
   auto left_items = left.DictItems();
@@ -355,14 +350,14 @@
 GuestOsRegistryService::Registration::~Registration() = default;
 
 std::string GuestOsRegistryService::Registration::DesktopFileId() const {
-  return pref_
-      .FindKeyOfType(guest_os::prefs::kAppDesktopFileIdKey,
-                     base::Value::Type::STRING)
-      ->GetString();
+  return GetString(guest_os::prefs::kAppDesktopFileIdKey);
 }
 
 GuestOsRegistryService::VmType GuestOsRegistryService::Registration::VmType()
     const {
+  if (!pref_.is_dict()) {
+    return GuestOsRegistryService::VmType::ApplicationList_VmType_TERMINA;
+  }
   // The VmType field is new, existing Apps that do not include it must be
   // TERMINA (0) Apps, as Plugin VM apps are not yet in production.
   return static_cast<GuestOsRegistryService::VmType>(
@@ -370,50 +365,38 @@
 }
 
 std::string GuestOsRegistryService::Registration::VmName() const {
-  return pref_
-      .FindKeyOfType(guest_os::prefs::kAppVmNameKey, base::Value::Type::STRING)
-      ->GetString();
+  return GetString(guest_os::prefs::kAppVmNameKey);
 }
 
 std::string GuestOsRegistryService::Registration::ContainerName() const {
-  return pref_
-      .FindKeyOfType(guest_os::prefs::kAppContainerNameKey,
-                     base::Value::Type::STRING)
-      ->GetString();
+  return GetString(guest_os::prefs::kAppContainerNameKey);
 }
 
 std::string GuestOsRegistryService::Registration::Name() const {
   if (VmType() == VmType::ApplicationList_VmType_PLUGIN_VM) {
     return l10n_util::GetStringFUTF8(
         IDS_PLUGIN_VM_APP_NAME_WINDOWS_SUFFIX,
-        base::UTF8ToUTF16(LocalizedString(guest_os::prefs::kAppNameKey)));
+        base::UTF8ToUTF16(GetLocalizedString(guest_os::prefs::kAppNameKey)));
   }
-  return LocalizedString(guest_os::prefs::kAppNameKey);
+  return GetLocalizedString(guest_os::prefs::kAppNameKey);
 }
 
 std::string GuestOsRegistryService::Registration::Comment() const {
-  return LocalizedString(guest_os::prefs::kAppCommentKey);
+  return GetLocalizedString(guest_os::prefs::kAppCommentKey);
 }
 
 std::string GuestOsRegistryService::Registration::Exec() const {
-  return pref_
-      .FindKeyOfType(guest_os::prefs::kAppExecKey, base::Value::Type::STRING)
-      ->GetString();
+  return GetString(guest_os::prefs::kAppExecKey);
 }
 
 std::string GuestOsRegistryService::Registration::ExecutableFileName() const {
-  if (pref_.is_none())
-    return std::string();
-  const base::Value* executable_file_name = pref_.FindKeyOfType(
-      guest_os::prefs::kAppExecutableFileNameKey, base::Value::Type::STRING);
-  if (!executable_file_name)
-    return std::string();
-  return executable_file_name->GetString();
+  return GetString(guest_os::prefs::kAppExecutableFileNameKey);
 }
 
 std::set<std::string> GuestOsRegistryService::Registration::Extensions() const {
-  if (pref_.is_none())
+  if (!pref_.is_dict()) {
     return {};
+  }
   // Convert to lowercase ASCII to allow case-insensitive match.
   return ListToStringSet(pref_.FindKeyOfType(guest_os::prefs::kAppExtensionsKey,
                                              base::Value::Type::LIST),
@@ -421,8 +404,9 @@
 }
 
 std::set<std::string> GuestOsRegistryService::Registration::MimeTypes() const {
-  if (pref_.is_none())
+  if (!pref_.is_dict()) {
     return {};
+  }
   // Convert to lowercase ASCII to allow case-insensitive match.
   return ListToStringSet(pref_.FindKeyOfType(guest_os::prefs::kAppMimeTypesKey,
                                              base::Value::Type::LIST),
@@ -430,32 +414,21 @@
 }
 
 std::set<std::string> GuestOsRegistryService::Registration::Keywords() const {
-  return LocalizedList(guest_os::prefs::kAppKeywordsKey);
+  return GetLocalizedList(guest_os::prefs::kAppKeywordsKey);
 }
 
 bool GuestOsRegistryService::Registration::NoDisplay() const {
-  if (pref_.is_none())
-    return false;
-  const base::Value* no_display = pref_.FindKeyOfType(
-      guest_os::prefs::kAppNoDisplayKey, base::Value::Type::BOOLEAN);
-  if (no_display)
-    return no_display->GetBool();
-  return false;
+  return GetBool(guest_os::prefs::kAppNoDisplayKey);
 }
 
 std::string GuestOsRegistryService::Registration::PackageId() const {
-  if (pref_.is_none())
-    return std::string();
-  const base::Value* package_id = pref_.FindKeyOfType(
-      guest_os::prefs::kAppPackageIdKey, base::Value::Type::STRING);
-  if (!package_id)
-    return std::string();
-  return package_id->GetString();
+  return GetString(guest_os::prefs::kAppPackageIdKey);
 }
 
 bool GuestOsRegistryService::Registration::CanUninstall() const {
-  if (pref_.is_none())
+  if (!pref_.is_dict()) {
     return false;
+  }
   // We can uninstall if and only if there is a package that owns the
   // application. If no package owns the application, we don't know how to
   // uninstall the app.
@@ -468,40 +441,78 @@
   // all.
   const base::Value* package_id = pref_.FindKeyOfType(
       guest_os::prefs::kAppPackageIdKey, base::Value::Type::STRING);
-  if (package_id)
+  if (package_id) {
     return !package_id->GetString().empty();
+  }
   return false;
 }
 
 base::Time GuestOsRegistryService::Registration::InstallTime() const {
-  return GetTime(pref_, guest_os::prefs::kAppInstallTimeKey);
+  return GetTime(guest_os::prefs::kAppInstallTimeKey);
 }
 
 base::Time GuestOsRegistryService::Registration::LastLaunchTime() const {
-  return GetTime(pref_, guest_os::prefs::kAppLastLaunchTimeKey);
+  return GetTime(guest_os::prefs::kAppLastLaunchTimeKey);
 }
 
 bool GuestOsRegistryService::Registration::IsScaled() const {
-  if (pref_.is_none())
+  return GetBool(guest_os::prefs::kAppScaledKey);
+}
+
+std::string GuestOsRegistryService::Registration::GetString(
+    base::StringPiece key) const {
+  if (!pref_.is_dict()) {
+    return std::string();
+  }
+  const base::Value* value =
+      pref_.FindKeyOfType(key, base::Value::Type::STRING);
+  if (!value) {
+    return std::string();
+  }
+  return value->GetString();
+}
+
+bool GuestOsRegistryService::Registration::GetBool(
+    base::StringPiece key) const {
+  if (!pref_.is_dict()) {
     return false;
-  const base::Value* scaled = pref_.FindKeyOfType(
-      guest_os::prefs::kAppScaledKey, base::Value::Type::BOOLEAN);
-  if (!scaled)
+  }
+  const base::Value* value =
+      pref_.FindKeyOfType(key, base::Value::Type::BOOLEAN);
+  if (!value) {
     return false;
-  return scaled->GetBool();
+  }
+  return value->GetBool();
+}
+
+// This is the companion to GuestOsRegistryService::SetCurrentTime().
+base::Time GuestOsRegistryService::Registration::GetTime(
+    base::StringPiece key) const {
+  if (!pref_.is_dict()) {
+    return base::Time();
+  }
+  const base::Value* value =
+      pref_.FindKeyOfType(key, base::Value::Type::STRING);
+  int64_t time;
+  if (!value || !base::StringToInt64(value->GetString(), &time)) {
+    return base::Time();
+  }
+  return base::Time::FromDeltaSinceWindowsEpoch(base::Microseconds(time));
 }
 
 // We store in prefs all the localized values for given fields (formatted with
 // undescores, e.g. 'fr' or 'en_US'), but users of the registry don't need to
 // deal with this.
-std::string GuestOsRegistryService::Registration::LocalizedString(
+std::string GuestOsRegistryService::Registration::GetLocalizedString(
     base::StringPiece key) const {
-  if (pref_.is_none())
+  if (!pref_.is_dict()) {
     return std::string();
+  }
   const base::Value* dict =
       pref_.FindKeyOfType(key, base::Value::Type::DICTIONARY);
-  if (!dict)
+  if (!dict) {
     return std::string();
+  }
 
   std::string current_locale =
       l10n_util::NormalizeLocale(g_browser_process->GetApplicationLocale());
@@ -513,20 +524,23 @@
   for (const std::string& locale : locales) {
     const base::Value* value =
         dict->FindKeyOfType(locale, base::Value::Type::STRING);
-    if (value)
+    if (value) {
       return value->GetString();
+    }
   }
   return std::string();
 }
 
-std::set<std::string> GuestOsRegistryService::Registration::LocalizedList(
+std::set<std::string> GuestOsRegistryService::Registration::GetLocalizedList(
     base::StringPiece key) const {
-  if (pref_.is_none())
+  if (!pref_.is_dict()) {
     return {};
+  }
   const base::Value* dict =
       pref_.FindKeyOfType(key, base::Value::Type::DICTIONARY);
-  if (!dict)
+  if (!dict) {
     return {};
+  }
 
   std::string current_locale =
       l10n_util::NormalizeLocale(g_browser_process->GetApplicationLocale());
@@ -538,8 +552,9 @@
   for (const std::string& locale : locales) {
     const base::Value* value =
         dict->FindKeyOfType(locale, base::Value::Type::LIST);
-    if (value)
+    if (value) {
       return ListToStringSet(value);
+    }
   }
   return {};
 }
@@ -588,8 +603,9 @@
   bool borealis_enabled = borealis::BorealisService::GetForProfile(profile_)
                               ->Features()
                               .IsEnabled();
-  if (!crostini_enabled && !plugin_vm_enabled && !borealis_enabled)
+  if (!crostini_enabled && !plugin_vm_enabled && !borealis_enabled) {
     return {};
+  }
 
   auto apps = GetAllRegisteredApps();
   for (auto it = apps.cbegin(); it != apps.cend();) {
@@ -642,8 +658,9 @@
 
   const base::Value* pref_registration =
       apps->FindKeyOfType(app_id, base::Value::Type::DICTIONARY);
-  if (!pref_registration)
+  if (!pref_registration) {
     return absl::nullopt;
+  }
   return absl::make_optional<Registration>(app_id, pref_registration->Clone());
 }
 
@@ -654,13 +671,15 @@
   base::flat_map<int, int> num_apps;
 
   for (const auto item : apps->DictItems()) {
-    if (item.first == crostini::kCrostiniTerminalSystemAppId)
+    if (item.first == crostini::kCrostiniTerminalSystemAppId) {
       continue;
+    }
 
     absl::optional<bool> no_display =
         item.second.FindBoolKey(guest_os::prefs::kAppNoDisplayKey);
-    if (no_display && no_display.value())
+    if (no_display && no_display.value()) {
       continue;
+    }
 
     int vm_type =
         item.second.FindIntKey(guest_os::prefs::kAppVmTypeKey).value_or(0);
@@ -891,13 +910,16 @@
     base::DictionaryValue* apps = update.Get();
 
     for (const auto item : apps->DictItems()) {
-      if (item.first == crostini::kCrostiniTerminalSystemAppId)
+      if (item.first == crostini::kCrostiniTerminalSystemAppId) {
         continue;
+      }
       Registration registration(item.first, item.second.Clone());
-      if (vm_type != registration.VmType())
+      if (vm_type != registration.VmType()) {
         continue;
-      if (vm_name != registration.VmName())
+      }
+      if (vm_name != registration.VmName()) {
         continue;
+      }
       if (!container_name.empty() &&
           container_name != registration.ContainerName()) {
         continue;
@@ -910,8 +932,9 @@
     }
   }
 
-  if (removed_apps.empty())
+  if (removed_apps.empty()) {
     return;
+  }
 
   std::vector<std::string> updated_apps;
   std::vector<std::string> inserted_apps;
@@ -969,8 +992,9 @@
           app_list.container_name(), app, std::move(name));
 
       base::Value* old_app = apps->FindKey(app_id);
-      if (old_app && EqualsExcludingTimestamps(pref_registration, *old_app))
+      if (old_app && EqualsExcludingTimestamps(pref_registration, *old_app)) {
         continue;
+      }
 
       base::Value* old_install_time = nullptr;
       base::Value* old_last_launch_time = nullptr;
@@ -984,11 +1008,12 @@
         inserted_apps.push_back(app_id);
       }
 
-      if (old_install_time)
+      if (old_install_time) {
         pref_registration.SetKey(guest_os::prefs::kAppInstallTimeKey,
                                  old_install_time->Clone());
-      else
+      } else {
         SetCurrentTime(&pref_registration, guest_os::prefs::kAppInstallTimeKey);
+      }
 
       if (old_last_launch_time) {
         pref_registration.SetKey(guest_os::prefs::kAppLastLaunchTimeKey,
@@ -999,8 +1024,9 @@
     }
 
     for (const auto item : apps->DictItems()) {
-      if (item.first == crostini::kCrostiniTerminalSystemAppId)
+      if (item.first == crostini::kCrostiniTerminalSystemAppId) {
         continue;
+      }
       if (item.second.FindKey(guest_os::prefs::kAppVmNameKey)->GetString() ==
               app_list.vm_name() &&
           item.second.FindKey(guest_os::prefs::kAppContainerNameKey)
@@ -1030,8 +1056,9 @@
   }
   retry_icon_requests_.clear();
 
-  if (updated_apps.empty() && removed_apps.empty() && inserted_apps.empty())
+  if (updated_apps.empty() && removed_apps.empty() && inserted_apps.empty()) {
     return;
+  }
 
   for (Observer& obs : observers_) {
     obs.OnRegistryUpdated(this, app_list.vm_type(), updated_apps, removed_apps,
diff --git a/chrome/browser/ash/guest_os/guest_os_registry_service.h b/chrome/browser/ash/guest_os/guest_os_registry_service.h
index 9eeb7f2..d2abf62 100644
--- a/chrome/browser/ash/guest_os/guest_os_registry_service.h
+++ b/chrome/browser/ash/guest_os/guest_os_registry_service.h
@@ -115,8 +115,11 @@
     bool CanUninstall() const;
 
    private:
-    std::string LocalizedString(base::StringPiece key) const;
-    std::set<std::string> LocalizedList(base::StringPiece key) const;
+    std::string GetString(base::StringPiece key) const;
+    bool GetBool(base::StringPiece key) const;
+    base::Time GetTime(base::StringPiece key) const;
+    std::string GetLocalizedString(base::StringPiece key) const;
+    std::set<std::string> GetLocalizedList(base::StringPiece key) const;
 
     std::string app_id_;
     base::Value pref_;
diff --git a/chrome/browser/ash/login/lock/screen_locker.cc b/chrome/browser/ash/login/lock/screen_locker.cc
index de84e20b8..aeed23c 100644
--- a/chrome/browser/ash/login/lock/screen_locker.cc
+++ b/chrome/browser/ash/login/lock/screen_locker.cc
@@ -618,10 +618,12 @@
   }
 
   if (!screen_locker_) {
-    ScreenLocker* locker =
-        new ScreenLocker(user_manager::UserManager::Get()->GetUnlockUsers());
-    VLOG(1) << "Created ScreenLocker " << locker;
-    locker->Init();
+    SessionControllerClientImpl::Get()->PrepareForLock(base::BindOnce([]() {
+      ScreenLocker* locker =
+          new ScreenLocker(user_manager::UserManager::Get()->GetUnlockUsers());
+      VLOG(1) << "Created ScreenLocker " << locker;
+      locker->Init();
+    }));
   } else {
     VLOG(1) << "ScreenLocker " << screen_locker_ << " already exists; "
             << " calling session manager's HandleLockScreenShown D-Bus method";
diff --git a/chrome/browser/ash/login/lock/screen_locker_browsertest.cc b/chrome/browser/ash/login/lock/screen_locker_browsertest.cc
index 2a6211c4..2063bbac 100644
--- a/chrome/browser/ash/login/lock/screen_locker_browsertest.cc
+++ b/chrome/browser/ash/login/lock/screen_locker_browsertest.cc
@@ -7,10 +7,6 @@
 #include <memory>
 
 #include "ash/constants/ash_pref_names.h"
-#include "ash/session/fullscreen_controller.h"
-#include "ash/session/fullscreen_notification_bubble.h"
-#include "ash/session/session_controller_impl.h"
-#include "ash/shell.h"
 #include "ash/wm/window_state.h"
 #include "base/bind.h"
 #include "base/run_loop.h"
@@ -34,7 +30,6 @@
 #include "content/public/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
-#include "ui/views/widget/widget.h"
 
 namespace ash {
 namespace {
@@ -88,16 +83,6 @@
     base::RunLoop().RunUntilIdle();
   }
 
-  bool IsFullscreenNotificationShowing() {
-    ash::FullscreenController* ash_fullscreen_controller =
-        Shell::Get()->session_controller()->fullscreen_controller_for_test();
-    return ash_fullscreen_controller->bubble_for_test() &&
-           ash_fullscreen_controller->bubble_for_test()->widget_for_test() &&
-           ash_fullscreen_controller->bubble_for_test()
-               ->widget_for_test()
-               ->IsVisible();
-  }
-
  private:
   void OnStartSession(const dbus::ObjectPath& path) {}
 
@@ -125,116 +110,71 @@
       1, session_manager_client()->notify_lock_screen_dismissed_call_count());
 }
 
-// Test that locking/unlocking the screen does not exit full screen if the
-// active window is in full screen mode and the full screen window does not have
-// all the pixels (e.g. immersive full screen where the shelf is auto hidden
-// instead of hidden).
-IN_PROC_BROWSER_TEST_F(ScreenLockerTest, BrowserFullscreenMode) {
+// Test how locking the screen affects an active fullscreen window.
+IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestFullscreenExit) {
+  // 1) If the active browser window is in fullscreen and the fullscreen window
+  // does not have all the pixels (e.g. the shelf is auto hidden instead of
+  // hidden), locking the screen should exit fullscreen. The shelf is
+  // auto hidden when in immersive fullscreen.
   ScreenLockerTester tester;
   BrowserWindow* browser_window = browser()->window();
   auto* window_state = WindowState::Get(browser_window->GetNativeWindow());
-
-  ::FullscreenController* fullscreen_controller =
-      browser()->exclusive_access_manager()->fullscreen_controller();
-  FullscreenNotificationObserver fullscreen_waiter(browser());
-
-  // Enter browser full screen mode.
-  fullscreen_controller->ToggleBrowserFullscreenMode();
-  fullscreen_waiter.Wait();
-  EXPECT_TRUE(browser_window->IsFullscreen());
-  EXPECT_FALSE(window_state->GetHideShelfWhenFullscreen());
-  EXPECT_FALSE(IsFullscreenNotificationShowing());
-
-  // Lock and unlock screen.
-  EXPECT_FALSE(tester.IsLocked());
-  tester.Lock();
-  EXPECT_TRUE(tester.IsLocked());
+  {
+    FullscreenNotificationObserver fullscreen_waiter(browser());
+    browser()
+        ->exclusive_access_manager()
+        ->fullscreen_controller()
+        ->ToggleBrowserFullscreenMode();
+    fullscreen_waiter.Wait();
+    EXPECT_TRUE(browser_window->IsFullscreen());
+    EXPECT_FALSE(window_state->GetHideShelfWhenFullscreen());
+    EXPECT_FALSE(tester.IsLocked());
+  }
+  {
+    tester.Lock();
+    EXPECT_FALSE(browser_window->IsFullscreen());
+    EXPECT_TRUE(window_state->GetHideShelfWhenFullscreen());
+    EXPECT_TRUE(tester.IsLocked());
+  }
   tester.SetUnlockPassword(user_manager::StubAccountId(), "pass");
   tester.UnlockWithPassword(user_manager::StubAccountId(), "pass");
   EXPECT_FALSE(tester.IsLocked());
+  EXPECT_FALSE(browser_window->IsFullscreen());
 
   // Browser window should be activated after screen locker is gone. Otherwise,
   // the rest of the test would fail.
   ASSERT_EQ(window_state, WindowState::ForActiveWindow());
 
-  // The browser window is still in full screen and a full screen notification
-  // is shown.
-  EXPECT_TRUE(browser_window->IsFullscreen());
-  EXPECT_FALSE(window_state->GetHideShelfWhenFullscreen());
-  EXPECT_TRUE(IsFullscreenNotificationShowing());
+  // 2) Similar to 1) if the active browser window is in fullscreen and the
+  // fullscreen window has all of the pixels, locking the screen should exit
+  // fullscreen. The fullscreen window has all of the pixels when in tab
+  // fullscreen.
+  {
+    FullscreenNotificationObserver fullscreen_waiter(browser());
+    content::WebContents* web_contents =
+        browser()->tab_strip_model()->GetActiveWebContents();
+    browser()
+        ->exclusive_access_manager()
+        ->fullscreen_controller()
+        ->EnterFullscreenModeForTab(web_contents->GetMainFrame());
+    fullscreen_waiter.Wait();
+    EXPECT_TRUE(browser_window->IsFullscreen());
+    EXPECT_TRUE(window_state->GetHideShelfWhenFullscreen());
+    EXPECT_FALSE(tester.IsLocked());
+  }
+  {
+    tester.Lock();
+    EXPECT_FALSE(browser_window->IsFullscreen());
+    EXPECT_TRUE(tester.IsLocked());
+  }
 
-  // Exiting full screen mode hides the notification.
-  fullscreen_controller->ToggleBrowserFullscreenMode();
-  fullscreen_waiter.Wait();
-  EXPECT_FALSE(browser_window->IsFullscreen());
-  EXPECT_FALSE(IsFullscreenNotificationShowing());
-
-  // Re-entering full screen mode does not re-show the full screen notification.
-  fullscreen_controller->ToggleBrowserFullscreenMode();
-  fullscreen_waiter.Wait();
-  EXPECT_TRUE(browser_window->IsFullscreen());
-  EXPECT_FALSE(IsFullscreenNotificationShowing());
-
-  EXPECT_EQ(1, session_manager_client()->notify_lock_screen_shown_call_count());
-  EXPECT_EQ(
-      1, session_manager_client()->notify_lock_screen_dismissed_call_count());
-}
-
-// Test that locking/unlocking the screen does not exit full screen if the
-// active window is in full screen mode and the full screen window has all of
-// the pixels (e.g. tab-initiated full screen mode).
-IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TabInitiatedFullscreenMode) {
-  ScreenLockerTester tester;
-  BrowserWindow* browser_window = browser()->window();
-  auto* window_state = WindowState::Get(browser_window->GetNativeWindow());
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-
-  ::FullscreenController* fullscreen_controller =
-      browser()->exclusive_access_manager()->fullscreen_controller();
-  FullscreenNotificationObserver fullscreen_waiter(browser());
-
-  // Enter tab-initiated full screen mode.
-  fullscreen_controller->EnterFullscreenModeForTab(
-      web_contents->GetMainFrame());
-  fullscreen_waiter.Wait();
-  EXPECT_TRUE(browser_window->IsFullscreen());
-  EXPECT_TRUE(window_state->GetHideShelfWhenFullscreen());
-  EXPECT_FALSE(IsFullscreenNotificationShowing());
-
-  // Lock and unlock screen.
-  EXPECT_FALSE(tester.IsLocked());
-  tester.Lock();
-  EXPECT_TRUE(tester.IsLocked());
   tester.SetUnlockPassword(user_manager::StubAccountId(), "pass");
   tester.UnlockWithPassword(user_manager::StubAccountId(), "pass");
   EXPECT_FALSE(tester.IsLocked());
 
-  // Browser window should be activated after screen locker is gone. Otherwise,
-  // the rest of the test would fail.
-  ASSERT_EQ(window_state, WindowState::ForActiveWindow());
-
-  // The browser window is still in full screen and a full screen notification
-  // is shown.
-  EXPECT_TRUE(browser_window->IsFullscreen());
-  EXPECT_TRUE(window_state->GetHideShelfWhenFullscreen());
-  EXPECT_TRUE(IsFullscreenNotificationShowing());
-
-  // Exiting full screen mode hides the notification.
-  fullscreen_controller->ExitFullscreenModeForTab(web_contents);
-  fullscreen_waiter.Wait();
-  EXPECT_FALSE(browser_window->IsFullscreen());
-  EXPECT_FALSE(IsFullscreenNotificationShowing());
-
-  // Re-entering full screen mode does not re-show the full screen notification.
-  fullscreen_controller->ToggleBrowserFullscreenMode();
-  fullscreen_waiter.Wait();
-  EXPECT_TRUE(browser_window->IsFullscreen());
-  EXPECT_FALSE(IsFullscreenNotificationShowing());
-
-  EXPECT_EQ(1, session_manager_client()->notify_lock_screen_shown_call_count());
+  EXPECT_EQ(2, session_manager_client()->notify_lock_screen_shown_call_count());
   EXPECT_EQ(
-      1, session_manager_client()->notify_lock_screen_dismissed_call_count());
+      2, session_manager_client()->notify_lock_screen_dismissed_call_count());
 }
 
 IN_PROC_BROWSER_TEST_F(ScreenLockerTest, TestShowTwice) {
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
index ed01d45..c6ea366 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.cc
@@ -157,9 +157,12 @@
 
   if (base::FeatureList::IsEnabled(kEnableNetworkTelemetryReporting)) {
     // Network health info.
-    CreateOneShotCollector(std::make_unique<NetworkInfoSampler>(),
-                           info_report_queue_.get(),
-                           ::ash::kReportDeviceNetworkConfiguration);
+    // ReportDeviceNetworkConfiguration policy is enabled by default, so set its
+    // default value to true.
+    CreateOneShotCollector(
+        std::make_unique<NetworkInfoSampler>(), info_report_queue_.get(),
+        /*enable_setting_path=*/::ash::kReportDeviceNetworkConfiguration,
+        /*setting_enabled_default_value=*/true);
   }
 }
 
@@ -199,7 +202,8 @@
 void MetricReportingManager::CreateOneShotCollector(
     std::unique_ptr<Sampler> sampler,
     MetricReportQueue* metric_report_queue,
-    const std::string& enable_setting_path) {
+    const std::string& enable_setting_path,
+    bool setting_enabled_default_value) {
   auto* const sampler_ptr = sampler.get();
   samplers_.emplace_back(std::move(sampler));
   if (!metric_report_queue) {
@@ -207,12 +211,13 @@
   }
   one_shot_collectors_.emplace_back(std::make_unique<OneShotCollector>(
       sampler_ptr, metric_report_queue, &reporting_settings_,
-      enable_setting_path));
+      enable_setting_path, setting_enabled_default_value));
 }
 
 void MetricReportingManager::CreatePeriodicCollector(
     std::unique_ptr<Sampler> sampler,
     const std::string& enable_setting_path,
+    bool setting_enabled_default_value,
     const std::string& rate_setting_path,
     base::TimeDelta default_rate,
     int rate_unit_to_ms) {
@@ -223,7 +228,8 @@
   }
   periodic_collectors_.emplace_back(std::make_unique<PeriodicCollector>(
       sampler_ptr, telemetry_report_queue_.get(), &reporting_settings_,
-      enable_setting_path, rate_setting_path, default_rate, rate_unit_to_ms));
+      enable_setting_path, setting_enabled_default_value, rate_setting_path,
+      default_rate, rate_unit_to_ms));
 }
 
 void MetricReportingManager::CreatePeriodicEventCollector(
@@ -231,6 +237,7 @@
     std::unique_ptr<EventDetector> event_detector,
     std::vector<Sampler*> additional_samplers,
     const std::string& enable_setting_path,
+    bool setting_enabled_default_value,
     const std::string& rate_setting_path,
     base::TimeDelta default_rate,
     int rate_unit_to_ms) {
@@ -242,12 +249,14 @@
   periodic_collectors_.emplace_back(std::make_unique<PeriodicEventCollector>(
       sampler_ptr, std::move(event_detector), std::move(additional_samplers),
       event_report_queue_.get(), &reporting_settings_, enable_setting_path,
-      rate_setting_path, default_rate, rate_unit_to_ms));
+      setting_enabled_default_value, rate_setting_path, default_rate,
+      rate_unit_to_ms));
 }
 
 void MetricReportingManager::CreateEventObserverManager(
     std::unique_ptr<MetricEventObserver> event_observer,
     const std::string& enable_setting_path,
+    bool setting_enabled_default_value,
     std::vector<Sampler*> additional_samplers) {
   if (!event_report_queue_) {
     return;
@@ -256,7 +265,7 @@
       std::make_unique<MetricEventObserverManager>(
           std::move(event_observer), event_report_queue_.get(),
           &reporting_settings_, enable_setting_path,
-          std::move(additional_samplers)));
+          setting_enabled_default_value, std::move(additional_samplers)));
 }
 
 void MetricReportingManager::InitNetworkCollectors() {
@@ -268,20 +277,28 @@
   auto network_telemetry_sampler =
       std::make_unique<NetworkTelemetrySampler>(https_latency_sampler.get());
   // Network health telemetry.
+  // ReportDeviceNetworkStatus policy is enabled by default, so set its default
+  // value to true.
+  const bool kReportDeviceNetworkStatusDefaultValue = true;
   CreatePeriodicCollector(
-      std::move(network_telemetry_sampler), ::ash::kReportDeviceNetworkStatus,
+      std::move(network_telemetry_sampler),
+      /*enable_setting_path=*/::ash::kReportDeviceNetworkStatus,
+      kReportDeviceNetworkStatusDefaultValue,
       ::ash::kReportDeviceNetworkTelemetryCollectionRateMs,
       GetDefaulCollectionRate(kDefaultNetworkTelemetryCollectionRate));
   // HttpsLatency events.
   CreatePeriodicEventCollector(
       std::move(https_latency_sampler),
       std::make_unique<HttpsLatencyEventDetector>(), /*additional_samplers=*/{},
-      ::ash::kReportDeviceNetworkStatus,
+      /*enable_setting_path=*/::ash::kReportDeviceNetworkStatus,
+      kReportDeviceNetworkStatusDefaultValue,
       ::ash::kReportDeviceNetworkTelemetryEventCheckingRateMs,
       GetDefaulEventCheckingRate(kDefaultNetworkTelemetryEventCheckingRate));
   // Network health events observer.
-  CreateEventObserverManager(std::make_unique<NetworkEventsObserver>(),
-                             ::ash::kReportDeviceNetworkStatus);
+  CreateEventObserverManager(
+      std::make_unique<NetworkEventsObserver>(),
+      /*enable_setting_path=*/::ash::kReportDeviceNetworkStatus,
+      kReportDeviceNetworkStatusDefaultValue);
 }
 
 }  // namespace reporting
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
index 319b1c18..b9e3640 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager.h
@@ -94,9 +94,11 @@
 
   void CreateOneShotCollector(std::unique_ptr<Sampler> sampler,
                               MetricReportQueue* report_queue,
-                              const std::string& enable_setting_path);
+                              const std::string& enable_setting_path,
+                              bool setting_enabled_default_value);
   void CreatePeriodicCollector(std::unique_ptr<Sampler> sampler,
                                const std::string& enable_setting_path,
+                               bool setting_enabled_default_value,
                                const std::string& rate_setting_path,
                                base::TimeDelta default_rate,
                                int rate_unit_to_ms = 1);
@@ -105,12 +107,14 @@
       std::unique_ptr<EventDetector> event_detector,
       std::vector<Sampler*> additional_samplers,
       const std::string& enable_setting_path,
+      bool setting_enabled_default_value,
       const std::string& rate_setting_path,
       base::TimeDelta default_rate,
       int rate_unit_to_ms = 1);
   void CreateEventObserverManager(
       std::unique_ptr<MetricEventObserver> event_observer,
       const std::string& enable_setting_path,
+      bool setting_enabled_default_value,
       std::vector<Sampler*> additional_samplers = {});
 
   void InitNetworkCollectors();
diff --git a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
index 66ede7e..9c48203 100644
--- a/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
+++ b/chrome/browser/ash/policy/reporting/metrics_reporting/metric_reporting_manager_unittest.cc
@@ -30,6 +30,7 @@
 #include "components/reporting/metrics/fake_sampler.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
 
 namespace reporting {
@@ -120,8 +121,9 @@
   bool is_feature_enabled;
   bool is_deprovisioned;
   bool is_affiliated;
-  bool info_policy_enabled;
-  bool telemetry_policy_enabled;
+  // optional to test the cases where the policies are not set.
+  absl::optional<bool> info_policy_enabled;
+  absl::optional<bool> telemetry_policy_enabled;
   RoutineVerdict latency_verdict;
   int expected_info_count;
   int expected_telemetry_count;
@@ -193,10 +195,16 @@
 TEST_P(NetworkHealthReportingTest, Info_Telemetry_LatencyEvent) {
   const NetworkHealthReportingTestCase& test_case = GetParam();
 
-  scoped_testing_cros_settings_.device_settings()->SetBoolean(
-      ::ash::kReportDeviceNetworkConfiguration, test_case.info_policy_enabled);
-  scoped_testing_cros_settings_.device_settings()->SetBoolean(
-      ::ash::kReportDeviceNetworkStatus, test_case.telemetry_policy_enabled);
+  if (test_case.info_policy_enabled.has_value()) {
+    scoped_testing_cros_settings_.device_settings()->SetBoolean(
+        ::ash::kReportDeviceNetworkConfiguration,
+        test_case.info_policy_enabled.value());
+  }
+  if (test_case.telemetry_policy_enabled.has_value()) {
+    scoped_testing_cros_settings_.device_settings()->SetBoolean(
+        ::ash::kReportDeviceNetworkStatus,
+        test_case.telemetry_policy_enabled.value());
+  }
   base::test::ScopedFeatureList scoped_feature_list;
   scoped_feature_list.InitWithFeatureState(
       MetricReportingManager::kEnableNetworkTelemetryReporting,
@@ -337,6 +345,22 @@
           /*expected_info_count=*/1,
           /*expected_telemetry_count=*/0, /*expected_event_count=*/0,
           /*expected_flush_per_period=*/1},
+         {"InfoPolicyDefaultEnabled", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/absl::nullopt,
+          /*telemetry_policy_enabled=*/false,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/1,
+          /*expected_telemetry_count=*/0, /*expected_event_count=*/0,
+          /*expected_flush_per_period=*/1},
+         {"TelemetryPolicyDefaultEnabled", /*is_feature_enabled=*/true,
+          /*is_deprovisioned=*/false,
+          /*is_affiliated=*/true, /*info_policy_enabled=*/false,
+          /*telemetry_policy_enabled=*/absl::nullopt,
+          /*latency_verdict=*/RoutineVerdict::PROBLEM,
+          /*expected_info_count=*/0,
+          /*expected_telemetry_count=*/1, /*expected_event_count=*/1,
+          /*expected_flush_per_period=*/1},
          {"LatencyVerdictNoProblem", /*is_feature_enabled=*/true,
           /*is_deprovisioned=*/false,
           /*is_affiliated=*/true, /*info_policy_enabled=*/true,
diff --git a/chrome/browser/ash/wallpaper_handlers/OWNERS b/chrome/browser/ash/wallpaper_handlers/OWNERS
new file mode 100644
index 0000000..ebb61881
--- /dev/null
+++ b/chrome/browser/ash/wallpaper_handlers/OWNERS
@@ -0,0 +1 @@
+file://ash/webui/personalization_app/OWNERS
diff --git a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
index 5f704efd..d2a3f06 100644
--- a/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
+++ b/chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc
@@ -204,7 +204,7 @@
   request.SerializeToString(&serialized_proto);
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("backdrop_collection_names_download",
+      net::DefineNetworkTrafficAnnotation("wallpaper_backdrop_collection_names",
                                           R"(
         semantics {
           sender: "ChromeOS Wallpaper Picker"
@@ -276,7 +276,7 @@
   request.SerializeToString(&serialized_proto);
 
   net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("backdrop_images_info_download", R"(
+      net::DefineNetworkTrafficAnnotation("wallpaper_backdrop_images_info", R"(
         semantics {
           sender: "ChromeOS Wallpaper Picker"
           description:
@@ -351,8 +351,9 @@
   request.SerializeToString(&serialized_proto);
 
   const net::NetworkTrafficAnnotationTag traffic_annotation =
-      net::DefineNetworkTrafficAnnotation("backdrop_surprise_me_image_download",
-                                          R"(
+      net::DefineNetworkTrafficAnnotation(
+          "wallpaper_backdrop_surprise_me_image",
+          R"(
         semantics {
           sender: "ChromeOS Wallpaper Picker"
           description:
diff --git a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
index b836e056..68c5beb 100644
--- a/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
+++ b/chrome/browser/ash/web_applications/projector_system_web_app_info.cc
@@ -5,11 +5,13 @@
 #include "chrome/browser/ash/web_applications/projector_system_web_app_info.h"
 
 #include "ash/constants/ash_features.h"
+#include "ash/constants/ash_pref_names.h"
 #include "ash/grit/ash_projector_app_trusted_resources.h"
 #include "ash/webui/projector_app/public/cpp/projector_app_constants.h"
 #include "chrome/browser/ash/web_applications/system_web_app_install_utils.h"
 #include "chrome/browser/policy/profile_policy_connector.h"
 #include "chrome/grit/generated_resources.h"
+#include "components/prefs/pref_service.h"
 #include "third_party/skia/include/core/SkColor.h"
 #include "ui/base/l10n/l10n_util.h"
 
@@ -55,7 +57,7 @@
     return ash::features::IsProjectorAllUserEnabled();
   }
 
-  // TODO(b/209675088): Check Projector admin policy.
-  return ash::features::IsProjectorEnabled() ||
-         ash::features::IsProjectorManagedUserEnabled();
+  // Check feature availability and admin policy for managed users.
+  return ash::features::IsProjectorEnabled() &&
+         profile_->GetPrefs()->GetBoolean(ash::prefs::kProjectorAllowByPolicy);
 }
diff --git a/chrome/browser/attribution_reporting/android/internal/BUILD.gn b/chrome/browser/attribution_reporting/android/internal/BUILD.gn
index fd656da..49ba405 100644
--- a/chrome/browser/attribution_reporting/android/internal/BUILD.gn
+++ b/chrome/browser/attribution_reporting/android/internal/BUILD.gn
@@ -4,6 +4,7 @@
   sources = [
     "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandlerFactory.java",
     "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionIntentHandlerImpl.java",
+    "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionMetrics.java",
     "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingJobFactory.java",
     "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTask.java",
     "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImpl.java",
@@ -14,8 +15,10 @@
     "java/src/org/chromium/chrome/browser/attribution_reporting/InputEventValidator.java",
     "java/src/org/chromium/chrome/browser/attribution_reporting/NoopAttributionIntentHandler.java",
   ]
+  annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
   deps = [
     "//base:base_java",
+    "//base:jni_java",
     "//chrome/android:base_module_java",
     "//chrome/browser/attribution_reporting/android:java",
     "//chrome/browser/flags:java",
@@ -31,6 +34,19 @@
   ]
 }
 
+source_set("internal") {
+  sources = [ "attribution_metrics_android.cc" ]
+
+  deps = [
+    ":jni_headers",
+    "//base",
+  ]
+}
+
+generate_jni("jni_headers") {
+  sources = [ "java/src/org/chromium/chrome/browser/attribution_reporting/AttributionMetrics.java" ]
+}
+
 android_library("junit_tests") {
   # Skip platform checks since Robolectric depends on requires_android targets.
   bypass_platform_checks = true
@@ -48,6 +64,7 @@
   deps = [
     ":java",
     "//base:base_java",
+    "//base:base_java_test_support",
     "//base:base_junit_test_support",
     "//chrome/android:base_module_java",
     "//chrome/browser/attribution_reporting/android:java",
diff --git a/chrome/browser/attribution_reporting/android/internal/attribution_metrics_android.cc b/chrome/browser/attribution_reporting/android/internal/attribution_metrics_android.cc
new file mode 100644
index 0000000..96bc6de0
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/internal/attribution_metrics_android.cc
@@ -0,0 +1,35 @@
+// 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/attribution_reporting/android/internal/jni_headers/AttributionMetrics_jni.h"
+
+#include "base/android/jni_string.h"
+#include "base/metrics/histogram.h"
+#include "base/metrics/histogram_base.h"
+
+using base::android::JavaParamRef;
+
+jlong JNI_AttributionMetrics_RecordEnumMetrics(
+    JNIEnv* env,
+    const JavaParamRef<jstring>& enum_name,
+    jlong histogram_ptr,
+    jint enum_value,
+    jint exclusive_max,
+    jint count) {
+  base::HistogramBase* histogram = nullptr;
+  if (histogram_ptr) {
+    histogram = reinterpret_cast<base::HistogramBase*>(histogram_ptr);
+  } else {
+    // See base::UmaHistogramExactLinear implementation. Since we may be adding
+    // a large number of samples at once, we have to get the histogram ourselves
+    // and use the AddCount method.
+    histogram = base::LinearHistogram::FactoryGet(
+        base::android::ConvertJavaStringToUTF8(env, enum_name), 1,
+        exclusive_max, exclusive_max + 1,
+        base::HistogramBase::kUmaTargetedHistogramFlag);
+  }
+
+  histogram->AddCount(enum_value, count);
+  return reinterpret_cast<jlong>(histogram);
+}
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionMetrics.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionMetrics.java
new file mode 100644
index 0000000..ef0c71d
--- /dev/null
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionMetrics.java
@@ -0,0 +1,57 @@
+// 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.
+
+package org.chromium.chrome.browser.attribution_reporting;
+
+import androidx.annotation.IntDef;
+
+import org.chromium.base.annotations.NativeMethods;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Manages Metrics for the attribution_reporting module.
+ */
+public class AttributionMetrics {
+    public static final String ATTRIBUTION_EVENTS_NAME = "Conversions.AppToWeb.AttributionEvents";
+
+    // It's safe to cache histogram pointers because they're never freed.
+    private static long sNativeAttibutionEventsPtr;
+
+    // These values are used for histograms, do not reorder.
+    @IntDef({AttributionEvent.RECEIVED_WITH_NATIVE, AttributionEvent.CACHED_PRE_NATIVE,
+            AttributionEvent.REPORTED_POST_NATIVE, AttributionEvent.DROPPED_STORAGE_FULL,
+            AttributionEvent.DROPPED_READ_FAILED, AttributionEvent.DROPPED_WRITE_FAILED,
+            AttributionEvent.FILE_CLOSE_FAILED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AttributionEvent {
+        int RECEIVED_WITH_NATIVE = 0;
+        int CACHED_PRE_NATIVE = 1;
+        int REPORTED_POST_NATIVE = 2;
+        int DROPPED_STORAGE_FULL = 3;
+        int DROPPED_WRITE_FAILED = 4;
+        int DROPPED_READ_FAILED = 5;
+        int FILE_CLOSE_FAILED = 6;
+
+        int NUM_ENTRIES = 7;
+    }
+
+    public static boolean isValidAttributionEventMetric(String metricName, int enumValue) {
+        return AttributionMetrics.ATTRIBUTION_EVENTS_NAME.equals(metricName)
+                && enumValue < AttributionEvent.NUM_ENTRIES;
+    }
+
+    public static void recordAttributionEvent(@AttributionEvent int event, int count) {
+        sNativeAttibutionEventsPtr =
+                AttributionMetricsJni.get().recordEnumMetrics(ATTRIBUTION_EVENTS_NAME,
+                        sNativeAttibutionEventsPtr, event, AttributionEvent.NUM_ENTRIES, count);
+    }
+
+    @NativeMethods
+    interface Natives {
+        long recordEnumMetrics(String metricName, long nativeMetricPtr, int enumValue,
+                int exclusiveMax, int count);
+    }
+}
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTask.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTask.java
index e1363f7..2da1d2ec 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTask.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTask.java
@@ -44,6 +44,8 @@
 
         @Override
         public void run() {
+            AttributionMetrics.recordAttributionEvent(
+                    AttributionMetrics.AttributionEvent.REPORTED_POST_NATIVE, 1);
             reportImpression(mBrowserContext, AttributionReporter.getInstance(), mParams);
         }
     }
@@ -62,7 +64,15 @@
     public boolean onStartTask(
             Context context, TaskParameters taskParameters, TaskFinishedCallback callback) {
         TraceEvent.startAsync(TASK_TRACE_NAME, 0);
+
         mCallback = callback;
+        final boolean browserStarted =
+                BrowserStartupController.getInstance().isFullBrowserStarted();
+        if (!browserStarted) {
+            BrowserStartupController.getInstance().startBrowserProcessesSync(
+                    LibraryProcessType.PROCESS_BROWSER, false);
+        }
+
         new AsyncTask<List<AttributionParameters>>() {
             @Override
             protected List<AttributionParameters> doInBackground() {
@@ -71,9 +81,7 @@
 
             @Override
             protected void onPostExecute(List<AttributionParameters> result) {
-                if (!BrowserStartupController.getInstance().isFullBrowserStarted()) {
-                    BrowserStartupController.getInstance().startBrowserProcessesSync(
-                            LibraryProcessType.PROCESS_BROWSER, false);
+                if (!browserStarted) {
                     // Browser wasn't started, so we don't have to worry about doing too much work
                     // on the UI thread.
                     flushSync(Profile.getLastUsedRegularProfile(), result);
@@ -92,6 +100,8 @@
         for (AttributionParameters params : paramsList) {
             reportImpression(browserContext, reporter, params);
         }
+        AttributionMetrics.recordAttributionEvent(
+                AttributionMetrics.AttributionEvent.REPORTED_POST_NATIVE, paramsList.size());
         taskFinished();
     }
 
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTaskTest.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTaskTest.java
index d7e1a4cb..4faa5e5f 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTaskTest.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderFlushTaskTest.java
@@ -28,6 +28,7 @@
 
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.task.PostTask;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.CallbackHelper;
@@ -162,7 +163,7 @@
 
     private void startTaskAndVerifyAttributions(BackgroundTask.TaskFinishedCallback callback,
             CallbackHelper callbackHelper) throws Exception {
-        mFlushTask.onStartTask(null, null, callback);
+        ThreadUtils.runOnUiThreadBlocking(() -> { mFlushTask.onStartTask(null, null, callback); });
 
         callbackHelper.waitForCallback(0);
         mInOrder.verify(mAttributionReporter)
@@ -173,5 +174,10 @@
                         eq(REPORT_TO_2), eq(EXPIRY_2), eq(EVENT_TIME_2));
         mInOrder.verifyNoMoreInteractions();
         Mockito.verifyNoMoreInteractions(mAttributionReporter);
+
+        Assert.assertEquals(2,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                        AttributionMetrics.AttributionEvent.REPORTED_POST_NATIVE));
     }
 }
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImpl.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImpl.java
index 95b2db8..a9ffb6b13c 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImpl.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImpl.java
@@ -140,6 +140,8 @@
     }
 
     private Uri insertOnUiThread(final AttributionParameters parameters) {
+        AttributionMetrics.recordAttributionEvent(
+                AttributionMetrics.AttributionEvent.RECEIVED_WITH_NATIVE, 1);
         AttributionReporter.getInstance().reportAppImpression(Profile.getLastUsedRegularProfile(),
                 parameters.getSourcePackageName(), parameters.getSourceEventId(),
                 parameters.getDestination(), parameters.getReportTo(), parameters.getExpiry(),
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImplTest.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImplTest.java
index 4136ea5..9942040 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImplTest.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/AttributionReportingProviderImplTest.java
@@ -30,6 +30,7 @@
 import org.chromium.base.ContextUtils;
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.library_loader.LibraryProcessType;
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.test.util.browser.Features;
@@ -98,6 +99,10 @@
                 .reportAppImpression(any(),
                         eq(ContextUtils.getApplicationContext().getPackageName()), eq(EVENT_ID),
                         eq(CONVERSION_URL), eq(REPORT_TO_URL), eq(EXPIRY), eq(0L));
+        Assert.assertEquals(1,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                        AttributionMetrics.AttributionEvent.RECEIVED_WITH_NATIVE));
     }
 
     @Test
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStore.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStore.java
index a7bdba68..8cd3a23 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStore.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStore.java
@@ -7,12 +7,15 @@
 import android.util.Pair;
 
 import org.chromium.base.Log;
-import org.chromium.chrome.browser.attribution_reporting.ImpressionPersistentStoreFileManager.FileProperties;
+import org.chromium.base.ThreadUtils;
+import org.chromium.chrome.browser.attribution_reporting.ImpressionPersistentStoreFileManager.AttributionFileProperties;
+import org.chromium.chrome.browser.attribution_reporting.ImpressionPersistentStoreFileManager.CachedEnumMetric;
 
 import java.io.Closeable;
 import java.io.DataInput;
 import java.io.DataOutput;
 import java.io.EOFException;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -89,8 +92,10 @@
                 stream = filePair.first;
                 long fileSize = filePair.second;
 
-                // TODO(https://crbug.com/1210171): Record metrics for dropped impressions.
-                if (fileSize >= MAX_STORAGE_BYTES_PER_PACKAGE) return true;
+                if (fileSize >= MAX_STORAGE_BYTES_PER_PACKAGE) {
+                    cacheAttributionEvent(AttributionMetrics.AttributionEvent.DROPPED_STORAGE_FULL);
+                    return true;
+                }
 
                 stream.writeUTF(parameters.getSourceEventId());
                 stream.writeUTF(parameters.getDestination());
@@ -101,21 +106,25 @@
                 // purposes).
                 stream.writeLong(System.currentTimeMillis());
                 stream.writeChar(SENTINEL);
+                cacheAttributionEvent(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE);
                 return fileSize + BYTES_PER_ATTRIBUTION_ESTIMATE >= STORAGE_FLUSH_THRESHOLD;
             } catch (Exception e) {
+                cacheAttributionEvent(AttributionMetrics.AttributionEvent.DROPPED_WRITE_FAILED);
                 Log.w(TAG, WRITE_FAILURE, e);
                 return false;
             } finally {
                 try {
                     if (stream != null) stream.close();
                 } catch (Exception e) {
+                    cacheAttributionEvent(AttributionMetrics.AttributionEvent.FILE_CLOSE_FAILED);
                     Log.w(TAG, WRITE_FAILURE, e);
                 }
             }
         }
     }
 
-    private void readImpressions(List<AttributionParameters> output, FileProperties<R> properties) {
+    private void readImpressions(
+            List<AttributionParameters> output, AttributionFileProperties<R> properties) {
         try {
             // If user downgraded Chrome and the schema changed, just discard the attributions.
             if (properties.version > VERSION) return;
@@ -134,7 +143,8 @@
                 long eventTime = properties.reader.readLong();
                 char sentinel = properties.reader.readChar();
                 if (sentinel != SENTINEL) {
-                    // TODO(https://crbug.com/1210171): Record metrics for dropped impressions.
+                    // Note that metrics for failed reads are captured in
+                    // getAndClearStoredImpressions().
                     Log.w(TAG, "Failed to read Impression data, data was corrupted.");
                     return;
                 }
@@ -144,7 +154,6 @@
                 output.add(params);
             }
         } catch (Exception e) {
-            // TODO(https://crbug.com/1210171): Record metrics for dropped impressions.
             Log.w(TAG, READ_FAILURE, e);
         } finally {
             try {
@@ -155,11 +164,39 @@
         }
     }
 
+    public void cacheAttributionEvent(@AttributionMetrics.AttributionEvent int event) {
+        try {
+            synchronized (sFileLock) {
+                mFileManager.incrementEnumMetric(AttributionMetrics.ATTRIBUTION_EVENTS_NAME, event);
+            }
+        } catch (IOException e) {
+        }
+    }
+
     public List<AttributionParameters> getAndClearStoredImpressions() {
+        ThreadUtils.assertOnBackgroundThread();
         List<AttributionParameters> parameters = new ArrayList<>();
+        int cachedAttributions = -1;
         synchronized (sFileLock) {
             try {
-                for (FileProperties<R> properties : mFileManager.getAllFiles()) {
+                for (CachedEnumMetric metric : mFileManager.getCachedEnumMetrics()) {
+                    if (AttributionMetrics.isValidAttributionEventMetric(
+                                metric.metricName, metric.enumValue)) {
+                        if (metric.enumValue
+                                == AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE) {
+                            cachedAttributions = metric.count;
+                        }
+                        AttributionMetrics.recordAttributionEvent(metric.enumValue, metric.count);
+                    } else {
+                        // Drop unrecognized metrics, probably caused by version skew.
+                    }
+                }
+            } catch (Exception e) {
+                Log.w(TAG, "Failed to record Attribution metrics.", e);
+            }
+            try {
+                for (AttributionFileProperties<R> properties :
+                        mFileManager.getAllAttributionFiles()) {
                     readImpressions(parameters, properties);
                 }
             } catch (Exception e) {
@@ -167,6 +204,11 @@
             }
             mFileManager.clearAllData();
         }
+        if (cachedAttributions > parameters.size()) {
+            AttributionMetrics.recordAttributionEvent(
+                    AttributionMetrics.AttributionEvent.DROPPED_READ_FAILED,
+                    cachedAttributions - parameters.size());
+        }
         return parameters;
     }
 }
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManager.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManager.java
index 44503316..8125dc3 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManager.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManager.java
@@ -20,8 +20,8 @@
  */
 public interface ImpressionPersistentStoreFileManager<W extends DataOutput & Closeable, R
                                                               extends DataInput & Closeable> {
-    public static class FileProperties<R> {
-        public FileProperties(R reader, String packageName, int version) {
+    public static class AttributionFileProperties<R> {
+        public AttributionFileProperties(R reader, String packageName, int version) {
             this.reader = reader;
             this.packageName = packageName;
             this.version = version;
@@ -32,6 +32,18 @@
         public int version;
     }
 
+    public static class CachedEnumMetric {
+        public CachedEnumMetric(String metricName, int enumValue, int count) {
+            this.metricName = metricName;
+            this.enumValue = enumValue;
+            this.count = count;
+        }
+
+        public String metricName;
+        public int enumValue;
+        public int count;
+    }
+
     /**
      * @param packageName tab packagename of the app that sent the Attribution.
      * @param version the schema version you would like the writer for.
@@ -45,7 +57,17 @@
      * @return The {@link FileProperties} for each file and the package the file stores Attribution
      *         data for. The caller is responsible for closing the DataInputStream.
      */
-    public List<FileProperties<R>> getAllFiles() throws IOException;
+    public List<AttributionFileProperties<R>> getAllAttributionFiles() throws IOException;
+
+    /**
+     * Increments by one the cached value for the specified metric.
+     */
+    public void incrementEnumMetric(String metricName, int enumValue) throws IOException;
+
+    /**
+     * Returns the current value of all cached enum metrics.
+     */
+    public List<CachedEnumMetric> getCachedEnumMetrics() throws IOException;
 
     /**
      * Clear all persisted Attribution Data files.
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManagerImpl.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManagerImpl.java
index f56bb56..21ccf13 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManagerImpl.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileManagerImpl.java
@@ -5,6 +5,7 @@
 package org.chromium.chrome.browser.attribution_reporting;
 
 import android.content.Context;
+import android.util.AtomicFile;
 import android.util.Pair;
 
 import org.chromium.base.ContextUtils;
@@ -27,57 +28,116 @@
         implements ImpressionPersistentStoreFileManager<DataOutputStream, DataInputStream> {
     private static final String TAG = "ImpressionFileManager";
 
-    private static final String sBaseDirName = "attribution_reporting_temporary_storage";
-    private static class BaseStorageDirectoryHolder {
-        private static File sDirectory =
+    private static final String sBaseDirName = "attribution_reporting";
+    private static final String sAttributionDataDirName =
+            sBaseDirName + File.separator + "attribution_data";
+    private static final String sEnumDirName = sBaseDirName + File.separator + "enums";
+    private static class DirectoryHolder {
+        private static File sBaseDirectory =
                 ContextUtils.getApplicationContext().getDir(sBaseDirName, Context.MODE_PRIVATE);
+        private static File sDataDirectory = new File(sBaseDirectory, sAttributionDataDirName);
+        private static File sEnumDirectory = new File(sBaseDirectory, sEnumDirName);
     }
 
     private static final char VERSION_DELIMITER = '$';
+    private static final char ENUM_DELIMITER = VERSION_DELIMITER;
 
     public ImpressionPersistentStoreFileManagerImpl() {}
 
-    private File getOrCreateBaseStorageDirectory() {
-        return BaseStorageDirectoryHolder.sDirectory;
-    }
-
     private String fileNameForPackage(String packageName, int version) {
         return packageName + VERSION_DELIMITER + version;
     }
 
+    private String fileNameForEnum(String metricName, int enumValue) {
+        return metricName + ENUM_DELIMITER + enumValue;
+    }
+
     @Override
     public Pair<DataOutputStream, Long> getForPackage(String packageName, int version)
             throws IOException {
-        File file = new File(
-                getOrCreateBaseStorageDirectory(), fileNameForPackage(packageName, version));
+        if (!DirectoryHolder.sDataDirectory.exists()) DirectoryHolder.sDataDirectory.mkdirs();
+        File file =
+                new File(DirectoryHolder.sDataDirectory, fileNameForPackage(packageName, version));
         long fileSize = file.length();
         if (fileSize == 0L) file.createNewFile();
-        return Pair.create(
-                new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file, true))),
+        return Pair.create(new DataOutputStream(new BufferedOutputStream(
+                                   new FileOutputStream(file, true /* append */))),
                 fileSize);
     }
 
     @Override
-    public List<FileProperties<DataInputStream>> getAllFiles() throws IOException {
-        File dir = getOrCreateBaseStorageDirectory();
-        List<FileProperties<DataInputStream>> fileProperties = new ArrayList<>();
-        for (File file : dir.listFiles()) {
+    public List<AttributionFileProperties<DataInputStream>> getAllAttributionFiles()
+            throws IOException {
+        List<AttributionFileProperties<DataInputStream>> fileProperties = new ArrayList<>();
+        if (!DirectoryHolder.sDataDirectory.exists()) return fileProperties;
+        for (File file : DirectoryHolder.sDataDirectory.listFiles()) {
             DataInputStream reader =
                     new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
             int delimiterPos = file.getName().lastIndexOf(VERSION_DELIMITER);
             assert delimiterPos != -1;
             String packageName = file.getName().substring(0, delimiterPos);
             int version = Integer.parseInt(file.getName().substring(delimiterPos + 1));
-            fileProperties.add(new FileProperties<DataInputStream>(reader, packageName, version));
+            fileProperties.add(
+                    new AttributionFileProperties<DataInputStream>(reader, packageName, version));
         }
         return fileProperties;
     }
 
     @Override
+    public void incrementEnumMetric(String metricName, int enumValue) throws IOException {
+        if (!DirectoryHolder.sEnumDirectory.exists()) DirectoryHolder.sEnumDirectory.mkdirs();
+        // Use AtomicFile to avoid losing metrics in case of kernel panics or power loss. This can
+        // end up being expensive at the long tail, but we don't have a better alternative.
+        File rawFile =
+                new File(DirectoryHolder.sEnumDirectory, fileNameForEnum(metricName, enumValue));
+        AtomicFile file = new AtomicFile(rawFile);
+        int currentValue = 0;
+        if (rawFile.length() > 0L) {
+            try (DataInputStream reader =
+                            new DataInputStream(new BufferedInputStream(file.openRead()))) {
+                currentValue = reader.readInt();
+            }
+        }
+        FileOutputStream output = file.startWrite();
+        try {
+            DataOutputStream writer = new DataOutputStream(new BufferedOutputStream(output));
+            writer.writeInt(currentValue + 1);
+            writer.flush();
+            file.finishWrite(output);
+        } catch (Exception e) {
+            file.failWrite(output);
+            throw e;
+        }
+    }
+
+    @Override
+    public List<CachedEnumMetric> getCachedEnumMetrics() throws IOException {
+        List<CachedEnumMetric> metrics = new ArrayList<>();
+        if (!DirectoryHolder.sEnumDirectory.exists()) return metrics;
+        for (File file : DirectoryHolder.sEnumDirectory.listFiles()) {
+            try (DataInputStream reader = new DataInputStream(
+                         new BufferedInputStream(new FileInputStream(file)))) {
+                int delimiterPos = file.getName().lastIndexOf(ENUM_DELIMITER);
+                assert delimiterPos != -1;
+                String metricName = file.getName().substring(0, delimiterPos);
+                int enumValue = Integer.parseInt(file.getName().substring(delimiterPos + 1));
+                int count = reader.readInt();
+                metrics.add(new CachedEnumMetric(metricName, enumValue, count));
+            }
+        }
+        return metrics;
+    }
+
+    @Override
     public void clearAllData() {
-        File dir = getOrCreateBaseStorageDirectory();
-        for (File file : dir.listFiles()) {
-            file.delete();
+        File[] files = DirectoryHolder.sDataDirectory.listFiles();
+        if (files != null) {
+            for (File file : files) file.delete();
+        }
+
+        files = DirectoryHolder.sEnumDirectory.listFiles();
+        if (files != null) {
+            for (File file : files) file.delete();
         }
     }
 }
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileTest.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileTest.java
index 2d6e0b5..a903b3a 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileTest.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreFileTest.java
@@ -14,8 +14,10 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Batch;
+import org.chromium.content_public.browser.test.NativeLibraryTestUtils;
 
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -49,10 +51,21 @@
 
     private ImpressionPersistentStore<DataOutputStream, DataInputStream> mImpressionStore;
 
+    private int mPreTestTotalHistogramCount;
+    private int mPreTestCachedHistogramCount;
+
     @Before
     public void setUp() {
+        NativeLibraryTestUtils.loadNativeLibraryNoBrowserProcess();
+
         mFileManager = new ImpressionPersistentStoreFileManagerImpl();
         mImpressionStore = new ImpressionPersistentStore<>(mFileManager);
+
+        mPreTestTotalHistogramCount = RecordHistogram.getHistogramTotalCountForTesting(
+                AttributionMetrics.ATTRIBUTION_EVENTS_NAME);
+        mPreTestCachedHistogramCount = RecordHistogram.getHistogramValueCountForTesting(
+                AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE);
     }
 
     @After
@@ -94,6 +107,13 @@
         // Check that attributions were cleared.
         params = mImpressionStore.getAndClearStoredImpressions();
         Assert.assertTrue(params.isEmpty());
+        Assert.assertEquals(2 + mPreTestCachedHistogramCount,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                        AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE));
+        Assert.assertEquals(2 + mPreTestTotalHistogramCount,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME));
     }
 
     @Test
@@ -120,5 +140,53 @@
         // Check that attributions were cleared.
         params = mImpressionStore.getAndClearStoredImpressions();
         Assert.assertTrue(params.isEmpty());
+        Assert.assertEquals(2 + mPreTestCachedHistogramCount,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                        AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE));
+        Assert.assertEquals(2 + mPreTestTotalHistogramCount,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME));
+    }
+
+    @Test
+    @SmallTest
+    public void testStoreImpressions_NoFilesWithMetrics() throws Exception {
+        int writeFailedCount = RecordHistogram.getHistogramValueCountForTesting(
+                AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                AttributionMetrics.AttributionEvent.DROPPED_WRITE_FAILED);
+        int storageFullCount = RecordHistogram.getHistogramValueCountForTesting(
+                AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                AttributionMetrics.AttributionEvent.DROPPED_STORAGE_FULL);
+        long before = System.currentTimeMillis();
+
+        mImpressionStore.cacheAttributionEvent(
+                AttributionMetrics.AttributionEvent.DROPPED_WRITE_FAILED);
+        mImpressionStore.cacheAttributionEvent(
+                AttributionMetrics.AttributionEvent.DROPPED_STORAGE_FULL);
+        mImpressionStore.cacheAttributionEvent(
+                AttributionMetrics.AttributionEvent.DROPPED_STORAGE_FULL);
+
+        List<AttributionParameters> params = mImpressionStore.getAndClearStoredImpressions();
+        Assert.assertTrue(params.isEmpty());
+
+        Assert.assertEquals(3 + mPreTestTotalHistogramCount,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME));
+
+        Assert.assertEquals(1 + writeFailedCount,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                        AttributionMetrics.AttributionEvent.DROPPED_WRITE_FAILED));
+
+        Assert.assertEquals(2 + storageFullCount,
+                RecordHistogram.getHistogramValueCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                        AttributionMetrics.AttributionEvent.DROPPED_STORAGE_FULL));
+        // Check that metrics were cleared.
+        params = mImpressionStore.getAndClearStoredImpressions();
+        Assert.assertEquals(3 + mPreTestTotalHistogramCount,
+                RecordHistogram.getHistogramTotalCountForTesting(
+                        AttributionMetrics.ATTRIBUTION_EVENTS_NAME));
     }
 }
diff --git a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreUnitTest.java b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreUnitTest.java
index 882046cd..a08d795 100644
--- a/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreUnitTest.java
+++ b/chrome/browser/attribution_reporting/android/internal/java/src/org/chromium/chrome/browser/attribution_reporting/ImpressionPersistentStoreUnitTest.java
@@ -6,6 +6,7 @@
 
 import static org.mockito.Mockito.anyLong;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
@@ -27,8 +28,11 @@
 import org.mockito.quality.Strictness;
 import org.robolectric.annotation.Config;
 
+import org.chromium.base.ThreadUtils;
 import org.chromium.base.test.BaseRobolectricTestRunner;
-import org.chromium.chrome.browser.attribution_reporting.ImpressionPersistentStoreFileManager.FileProperties;
+import org.chromium.base.test.util.JniMocker;
+import org.chromium.chrome.browser.attribution_reporting.ImpressionPersistentStoreFileManager.AttributionFileProperties;
+import org.chromium.chrome.browser.attribution_reporting.ImpressionPersistentStoreFileManager.CachedEnumMetric;
 
 import java.io.Closeable;
 import java.io.DataInput;
@@ -65,6 +69,9 @@
     @Rule
     public MockitoRule mMockitoRule = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
+    @Rule
+    public final JniMocker mJniMocker = new JniMocker();
+
     interface Writer extends DataOutput, Closeable {}
     interface Reader extends DataInput, Closeable {}
 
@@ -80,11 +87,16 @@
     @Mock
     private ImpressionPersistentStoreFileManager<Writer, Reader> mFileManager;
 
+    @Mock
+    private AttributionMetrics.Natives mMetricsJni;
+
     private ImpressionPersistentStore<Writer, Reader> mImpressionStore;
     private InOrder mInOrder;
 
     @Before
     public void setUp() {
+        ThreadUtils.setThreadAssertsDisabledForTesting(true);
+        mJniMocker.mock(AttributionMetricsJni.TEST_HOOKS, mMetricsJni);
         mImpressionStore = new ImpressionPersistentStore(mFileManager);
         mInOrder = inOrder(mOutputStream, mInputStream1, mInputStream2);
     }
@@ -104,6 +116,10 @@
         mInOrder.verify(mOutputStream).close();
         mInOrder.verifyNoMoreInteractions();
         Mockito.verifyNoMoreInteractions(mOutputStream);
+
+        Mockito.verify(mFileManager, Mockito.times(1))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE));
     }
 
     @Test
@@ -122,6 +138,10 @@
         mInOrder.verify(mOutputStream).close();
         mInOrder.verifyNoMoreInteractions();
         Mockito.verifyNoMoreInteractions(mOutputStream);
+
+        Mockito.verify(mFileManager, Mockito.times(1))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE));
     }
 
     @Test
@@ -134,6 +154,12 @@
         mInOrder.verify(mOutputStream).close();
         mInOrder.verifyNoMoreInteractions();
         Mockito.verifyNoMoreInteractions(mOutputStream);
+        Mockito.verify(mFileManager, Mockito.times(1))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.DROPPED_STORAGE_FULL));
+        Mockito.verify(mFileManager, Mockito.times(0))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE));
     }
 
     @Test
@@ -147,6 +173,12 @@
         mInOrder.verify(mOutputStream).close();
         mInOrder.verifyNoMoreInteractions();
         Mockito.verifyNoMoreInteractions(mOutputStream);
+        Mockito.verify(mFileManager, Mockito.times(1))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.DROPPED_WRITE_FAILED));
+        Mockito.verify(mFileManager, Mockito.times(0))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE));
     }
 
     @Test
@@ -157,37 +189,76 @@
         Assert.assertFalse(mImpressionStore.storeImpression(PARAMS_1));
         mInOrder.verifyNoMoreInteractions();
         Mockito.verifyNoMoreInteractions(mOutputStream);
+        Mockito.verify(mFileManager, Mockito.times(1))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.DROPPED_WRITE_FAILED));
+        Mockito.verify(mFileManager, Mockito.times(0))
+                .incrementEnumMetric(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE));
     }
 
     @Test
     @SmallTest
     public void testReadImpressions_GetFileException() throws Exception {
-        when(mFileManager.getAllFiles()).thenThrow(new IOException());
+        List<CachedEnumMetric> metrics = new ArrayList<CachedEnumMetric>();
+        metrics.add(new CachedEnumMetric(AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE, 1234));
+        when(mFileManager.getCachedEnumMetrics()).thenReturn(metrics);
+
+        when(mFileManager.getAllAttributionFiles()).thenThrow(new IOException());
         List<AttributionParameters> params = mImpressionStore.getAndClearStoredImpressions();
         Assert.assertTrue(params.isEmpty());
+
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(1234));
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.DROPPED_READ_FAILED),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(1234));
     }
 
     @Test
     @SmallTest
     public void testReadImpressions_VersionTooNew() throws Exception {
-        List<FileProperties<Reader>> files = new ArrayList<>();
-        files.add(new FileProperties<Reader>(
+        List<CachedEnumMetric> metrics = new ArrayList<CachedEnumMetric>();
+        metrics.add(new CachedEnumMetric(AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE, 1234));
+        when(mFileManager.getCachedEnumMetrics()).thenReturn(metrics);
+
+        List<AttributionFileProperties<Reader>> files = new ArrayList<>();
+        files.add(new AttributionFileProperties<Reader>(
                 mInputStream1, PACKAGE_1, ImpressionPersistentStore.VERSION + 1));
-        when(mFileManager.getAllFiles()).thenReturn(files);
+        when(mFileManager.getAllAttributionFiles()).thenReturn(files);
         List<AttributionParameters> params = mImpressionStore.getAndClearStoredImpressions();
         mInOrder.verify(mInputStream1).close();
         mInOrder.verifyNoMoreInteractions();
         Mockito.verifyNoMoreInteractions(mInputStream1);
         Assert.assertTrue(params.isEmpty());
+
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(1234));
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.DROPPED_READ_FAILED),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(1234));
     }
 
     @Test
     @SmallTest
     public void testReadImpressions_Multiple_SingleFile() throws Exception {
-        List<FileProperties<Reader>> files = new ArrayList<>();
-        files.add(new FileProperties<Reader>(
+        List<CachedEnumMetric> metrics = new ArrayList<CachedEnumMetric>();
+        metrics.add(new CachedEnumMetric(AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE, 3));
+        when(mFileManager.getCachedEnumMetrics()).thenReturn(metrics);
+
+        List<AttributionFileProperties<Reader>> files = new ArrayList<>();
+        files.add(new AttributionFileProperties<Reader>(
                 mInputStream1, PACKAGE_1, ImpressionPersistentStore.VERSION));
-        when(mFileManager.getAllFiles()).thenReturn(files);
+        when(mFileManager.getAllAttributionFiles()).thenReturn(files);
         when(mInputStream1.readUTF())
                 .thenReturn(EVENT_ID_1)
                 .thenReturn(DESTINATION_1)
@@ -223,17 +294,31 @@
         Assert.assertEquals(AttributionParameters.forCachedEvent(PACKAGE_1, EVENT_ID_2,
                                     DESTINATION_2, "", EXPIRY_2, EVENT_TIME_2),
                 params.get(1));
+
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(3));
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.DROPPED_READ_FAILED),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(1));
     }
 
     @Test
     @SmallTest
     public void testReadImpressions_Multiple_TwoFiles() throws Exception {
-        List<FileProperties<Reader>> files = new ArrayList<>();
-        files.add(new FileProperties<Reader>(
+        List<CachedEnumMetric> metrics = new ArrayList<CachedEnumMetric>();
+        metrics.add(new CachedEnumMetric(AttributionMetrics.ATTRIBUTION_EVENTS_NAME,
+                AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE, 4));
+        when(mFileManager.getCachedEnumMetrics()).thenReturn(metrics);
+
+        List<AttributionFileProperties<Reader>> files = new ArrayList<>();
+        files.add(new AttributionFileProperties<Reader>(
                 mInputStream1, PACKAGE_1, ImpressionPersistentStore.VERSION));
-        files.add(new FileProperties<Reader>(
+        files.add(new AttributionFileProperties<Reader>(
                 mInputStream2, PACKAGE_2, ImpressionPersistentStore.VERSION));
-        when(mFileManager.getAllFiles()).thenReturn(files);
+        when(mFileManager.getAllAttributionFiles()).thenReturn(files);
         when(mInputStream1.readUTF())
                 .thenReturn(EVENT_ID_1)
                 .thenReturn(DESTINATION_1)
@@ -274,15 +359,24 @@
         Assert.assertEquals(AttributionParameters.forCachedEvent(PACKAGE_2, EVENT_ID_2,
                                     DESTINATION_2, "", EXPIRY_2, EVENT_TIME_2),
                 params.get(1));
+
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.CACHED_PRE_NATIVE),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(4));
+        Mockito.verify(mMetricsJni, Mockito.times(1))
+                .recordEnumMetrics(eq(AttributionMetrics.ATTRIBUTION_EVENTS_NAME), eq(0L),
+                        eq(AttributionMetrics.AttributionEvent.DROPPED_READ_FAILED),
+                        eq(AttributionMetrics.AttributionEvent.NUM_ENTRIES), eq(2));
     }
 
     @Test
     @SmallTest
     public void testReadImpressions_Multiple_Corruption() throws Exception {
-        List<FileProperties<Reader>> files = new ArrayList<>();
-        files.add(new FileProperties<Reader>(
+        List<AttributionFileProperties<Reader>> files = new ArrayList<>();
+        files.add(new AttributionFileProperties<Reader>(
                 mInputStream1, PACKAGE_1, ImpressionPersistentStore.VERSION));
-        when(mFileManager.getAllFiles()).thenReturn(files);
+        when(mFileManager.getAllAttributionFiles()).thenReturn(files);
         when(mInputStream1.readUTF())
                 .thenReturn(EVENT_ID_1)
                 .thenReturn(DESTINATION_1)
@@ -321,10 +415,10 @@
     @Test
     @SmallTest
     public void testReadImpressions_Multiple_Truncation() throws Exception {
-        List<FileProperties<Reader>> files = new ArrayList<>();
-        files.add(new FileProperties<Reader>(
+        List<AttributionFileProperties<Reader>> files = new ArrayList<>();
+        files.add(new AttributionFileProperties<Reader>(
                 mInputStream1, PACKAGE_1, ImpressionPersistentStore.VERSION));
-        when(mFileManager.getAllFiles()).thenReturn(files);
+        when(mFileManager.getAllAttributionFiles()).thenReturn(files);
         when(mInputStream1.readUTF())
                 .thenReturn(EVENT_ID_1)
                 .thenReturn(DESTINATION_1)
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc
index 3f93962..76de414 100644
--- a/chrome/browser/chrome_content_browser_client.cc
+++ b/chrome/browser/chrome_content_browser_client.cc
@@ -6338,6 +6338,15 @@
 
 bool ChromeContentBrowserClient::ShouldPreconnectNavigation(
     content::BrowserContext* browser_context) {
+#if BUILDFLAG(ENABLE_EXTENSIONS)
+  // An extension could be blocking connections for privacy reasons, so skip
+  // optimization if there are any extensions with WebRequest permissions.
+  const auto* web_request_api =
+      extensions::BrowserContextKeyedAPIFactory<extensions::WebRequestAPI>::Get(
+          browser_context);
+  if (web_request_api->MayHaveProxies())
+    return false;
+#endif
   return prefetch::IsSomePreloadingEnabled(
       *Profile::FromBrowserContext(browser_context)->GetPrefs());
 }
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 940c73e..c5f4c03 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -4704,11 +4704,6 @@
     "expiry_milestone": 99
   },
   {
-    "name": "raw-clipboard",
-    "owners": [ "huangdarwin", "pwnall" ],
-    "expiry_milestone": 94
-  },
-  {
     "name": "read-later",
     "owners": [ "chrome-desktop-ui-sea@google.com", "corising", "wylieb", "chrome-collections@google.com" ],
     "expiry_milestone": 102
diff --git a/chrome/browser/flags/android/chrome_feature_list.cc b/chrome/browser/flags/android/chrome_feature_list.cc
index 2f25277..d5d29c2 100644
--- a/chrome/browser/flags/android/chrome_feature_list.cc
+++ b/chrome/browser/flags/android/chrome_feature_list.cc
@@ -102,6 +102,7 @@
     &download::features::kSmartSuggestionForLargeDownloads,
     &download::features::kUseDownloadOfflineContentProvider,
     &embedder_support::kShowTrustedPublisherURL,
+    &features::kAnonymousUpdateChecks,
     &features::kContinuousSearch,
     &features::kEarlyLibraryLoad,
     &features::kGenericSensorExtraClasses,
diff --git a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
index af6a878c..90291c3 100644
--- a/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
+++ b/chrome/browser/flags/android/java/src/org/chromium/chrome/browser/flags/ChromeFeatureList.java
@@ -216,6 +216,7 @@
             "AndroidLayoutChangeTabReparenting";
     public static final String ANDROID_SEARCH_ENGINE_CHOICE_NOTIFICATION =
             "AndroidSearchEngineChoiceNotification";
+    public static final String ANONYMOUS_UPDATE_CHECKS = "AnonymousUpdateChecks";
     public static final String APP_LANGUAGE_PROMPT = "AppLanguagePrompt";
     public static final String ASSISTANT_CONSENT_SIMPLIFIED_TEXT = "AssistantConsentSimplifiedText";
     public static final String ASSISTANT_CONSENT_V2 = "AssistantConsentV2";
diff --git a/chrome/browser/font_prewarmer_tab_helper.cc b/chrome/browser/font_prewarmer_tab_helper.cc
index 3c878da..e38e0077 100644
--- a/chrome/browser/font_prewarmer_tab_helper.cc
+++ b/chrome/browser/font_prewarmer_tab_helper.cc
@@ -29,6 +29,7 @@
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/render_process_host_observer.h"
 #include "content/public/browser/web_contents.h"
+#include "content/public/common/child_process_host.h"
 #include "mojo/public/cpp/bindings/remote.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
@@ -209,6 +210,25 @@
              navigation_handle->GetURL());
 }
 
+void FontPrewarmerTabHelper::DidStartNavigation(
+    content::NavigationHandle* navigation_handle) {
+  if (!IsSearchResultsPageNavigation(navigation_handle))
+    return;
+
+  const int expected_render_process_host_id =
+      navigation_handle->GetExpectedRenderProcessHostId();
+  if (expected_render_process_host_id ==
+      content::ChildProcessHost::kInvalidUniqueID) {
+    expected_render_process_host_id_.reset();
+  } else {
+    expected_render_process_host_id_ = expected_render_process_host_id;
+    content::RenderProcessHost* rph =
+        content::RenderProcessHost::FromID(expected_render_process_host_id);
+    DCHECK(rph);
+    FontPrewarmerCoordinator::ForProfile(GetProfile()).SendFontsToPrewarm(rph);
+  }
+}
+
 void FontPrewarmerTabHelper::ReadyToCommitNavigation(
     content::NavigationHandle* navigation_handle) {
   if (!IsSearchResultsPageNavigation(navigation_handle))
@@ -218,7 +238,8 @@
   DCHECK(rfh);
   FontPrewarmerCoordinator& coordinator =
       FontPrewarmerCoordinator::ForProfile(GetProfile());
-  coordinator.SendFontsToPrewarm(rfh->GetProcess());
+  if (expected_render_process_host_id_ != rfh->GetProcess()->GetID())
+    coordinator.SendFontsToPrewarm(rfh->GetProcess());
   coordinator.RequestFonts(rfh);
 }
 
diff --git a/chrome/browser/font_prewarmer_tab_helper.h b/chrome/browser/font_prewarmer_tab_helper.h
index ec0147ca..af75ae1f 100644
--- a/chrome/browser/font_prewarmer_tab_helper.h
+++ b/chrome/browser/font_prewarmer_tab_helper.h
@@ -49,9 +49,13 @@
       content::NavigationHandle* navigation_handle);
 
   // content::WebContentsObserver implementation.
+  void DidStartNavigation(
+      content::NavigationHandle* navigation_handle) override;
   void ReadyToCommitNavigation(
       content::NavigationHandle* navigation_handle) override;
 
+  absl::optional<int> expected_render_process_host_id_;
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 };
 
diff --git a/chrome/browser/resources/chromeos/login/BUILD.gn b/chrome/browser/resources/chromeos/login/BUILD.gn
index f169525..b4c87562 100644
--- a/chrome/browser/resources/chromeos/login/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/BUILD.gn
@@ -169,8 +169,6 @@
     "components/oobe_slide/oobe_slide.js",
     "components/progress_list_item/progress_list_item.html",
     "components/progress_list_item/progress_list_item.js",
-    "components/throbber_notice.html",
-    "components/throbber_notice.js",
     "components/hd_iron_icon.html",
     "components/hd_iron_icon.js",
     "components/html_echo.html",
@@ -196,6 +194,10 @@
     "components/oobe_i18n_dropdown.js",
     "components/oobe_select.html",
     "components/oobe_types.html",
+    "components/security_token_pin.html",
+    "components/security_token_pin.js",
+    "components/throbber_notice.html",
+    "components/throbber_notice.js",
     "components/web_view_helper.html",
     "components/web_view_loader.html",
     "screens/common/adb_sideloading.html",
@@ -343,13 +345,14 @@
     "components/oobe_carousel/oobe_carousel.m.js",
     "components/oobe_slide/oobe_slide.m.js",
     "components/oobe_vars/oobe_shared_vars_css.m.js",
+    "components/progress_list_item/progress_list_item.m.js",
     "components/display_manager_types.m.js",
-    "components/hd_iron_icon.m.js",
-    "components/html_echo.m.js",
-    "components/long_touch_detector.m.js",
     "components/gaia_header.m.js",
     "components/gaia_button.m.js",
     "components/gaia_input_form.m.js",
+    "components/hd_iron_icon.m.js",
+    "components/html_echo.m.js",
+    "components/long_touch_detector.m.js",
     "components/network_select_login.m.js",
     "components/notification_card.m.js",
     "components/oobe_a11y_option.m.js",
@@ -358,7 +361,7 @@
     "components/oobe_icons.m.js",
     "components/oobe_select.m.js",
     "components/oobe_types.m.js",
-    "components/progress_list_item/progress_list_item.m.js",
+    "components/security_token_pin.m.js",
     "components/throbber_notice.m.js",
     "components/web_view_helper.m.js",
     "components/web_view_loader.m.js",
diff --git a/chrome/browser/resources/chromeos/login/components/BUILD.gn b/chrome/browser/resources/chromeos/login/components/BUILD.gn
index 94debfd..013fd82 100644
--- a/chrome/browser/resources/chromeos/login/components/BUILD.gn
+++ b/chrome/browser/resources/chromeos/login/components/BUILD.gn
@@ -59,6 +59,7 @@
     ":oobe_network_icons.m",
     ":oobe_select.m",
     ":oobe_types.m",
+    ":security_token_pin.m",
     ":throbber_notice.m",
     ":web_view_helper.m",
     ":web_view_loader.m",
@@ -92,6 +93,7 @@
                   ":oobe_a11y_option_module",
                   ":oobe_icons_module",
                   ":oobe_network_icons_module",
+                  ":security_token_pin_module",
                   ":throbber_notice_module",
                 ] + [ ":modulize" ]
 }
@@ -267,12 +269,16 @@
 
 js_library("throbber_notice.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/components/throbber_notice.m.js" ]
-  deps = [
-    "./behaviors:oobe_i18n_behavior",
-  ]
+  deps = [ "./behaviors:oobe_i18n_behavior" ]
   extra_deps = [ ":throbber_notice_module" ]
 }
 
+js_library("security_token_pin.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/components/security_token_pin.m.js" ]
+  deps = [ "./behaviors:oobe_i18n_behavior" ]
+  extra_deps = [ ":security_token_pin_module" ]
+}
+
 js_library("web_view_helper.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/chromeos/login/components/web_view_helper.m.js" ]
   deps = []
@@ -379,6 +385,14 @@
   html_type = "iron-iconset"
 }
 
+polymer_modulizer("security_token_pin") {
+  js_file = "security_token_pin.js"
+  html_file = "security_token_pin.html"
+  html_type = "dom-module"
+  auto_imports = oobe_auto_imports
+  namespace_rewrites = oobe_namespace_rewrites
+}
+
 polymer_modulizer("throbber_notice") {
   js_file = "throbber_notice.js"
   html_file = "throbber_notice.html"
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.html b/chrome/browser/resources/chromeos/login/components/security_token_pin.html
similarity index 84%
rename from chrome/browser/resources/chromeos/login/security_token_pin.html
rename to chrome/browser/resources/chromeos/login/components/security_token_pin.html
index a1c0b1e..48e5dfe9 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin.html
+++ b/chrome/browser/resources/chromeos/login/components/security_token_pin.html
@@ -7,14 +7,14 @@
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
 <link rel="import" href="chrome://resources/cr_elements/icons.html">
 
-<link rel="import" href="/components/oobe_icons.html">
-<link rel="import" href="/components/behaviors/oobe_dialog_host_behavior.html">
-<link rel="import" href="/components/behaviors/oobe_i18n_behavior.html">
-<link rel="import" href="/components/buttons/oobe_back_button.html">
-<link rel="import" href="/components/buttons/oobe_next_button.html">
-<link rel="import" href="/components/common_styles/common_styles.html">
-<link rel="import" href="/components/common_styles/oobe_dialog_host_styles.html">
-<link rel="import" href="/components/dialogs/oobe_adaptive_dialog.html">
+<link rel="import" href="./oobe_icons.html">
+<link rel="import" href="./behaviors/oobe_dialog_host_behavior.html">
+<link rel="import" href="./behaviors/oobe_i18n_behavior.html">
+<link rel="import" href="./buttons/oobe_back_button.html">
+<link rel="import" href="./buttons/oobe_next_button.html">
+<link rel="import" href="./common_styles/common_styles.html">
+<link rel="import" href="./common_styles/oobe_dialog_host_styles.html">
+<link rel="import" href="./dialogs/oobe_adaptive_dialog.html">
 
 <dom-module id="security-token-pin">
   <template>
@@ -93,4 +93,5 @@
       </div>
     </oobe-adaptive-dialog>
   </template>
+  <script src="security_token_pin.js"></script>
 </dom-module>
diff --git a/chrome/browser/resources/chromeos/login/security_token_pin.js b/chrome/browser/resources/chromeos/login/components/security_token_pin.js
similarity index 60%
rename from chrome/browser/resources/chromeos/login/security_token_pin.js
rename to chrome/browser/resources/chromeos/login/components/security_token_pin.js
index 6802f1d..5594690 100644
--- a/chrome/browser/resources/chromeos/login/security_token_pin.js
+++ b/chrome/browser/resources/chromeos/login/components/security_token_pin.js
@@ -7,83 +7,96 @@
  * sign-in.
  */
 
-(function() {
+/* #js_imports_placeholder */
 
-Polymer({
-  is: 'security-token-pin',
+/**
+ * @constructor
+ * @extends {PolymerElement}
+ * @implements {OobeI18nBehaviorInterface}
+ * @implements {OobeDialogHostBehaviorInterface}
+ */
+const SecurityTokenPinBase = Polymer.mixinBehaviors(
+    [OobeI18nBehavior, OobeDialogHostBehavior], Polymer.Element);
 
-  behaviors: [OobeI18nBehavior, OobeDialogHostBehavior],
+class SecurityTokenPin extends SecurityTokenPinBase {
+  static get is() {
+    return 'security-token-pin';
+  }
 
-  properties: {
-    /**
-     * Contains the OobeTypes.SecurityTokenPinDialogParameters object. It can be
-     * null when our element isn't used.
-     *
-     * Changing this field resets the dialog state. (Please note that, due to
-     * the Polymer's limitation, only assigning a new object is observed;
-     * changing just a subproperty won't work.)
-     */
-    parameters: {
-      type: Object,
-      observer: 'onParametersChanged_',
-    },
+  /* #html_template_placeholder */
 
-    /**
-     * Whether the current state is the wait for the processing completion
-     * (i.e., the backend is verifying the entered PIN).
-     * @private
-     */
-    processingCompletion_: {
-      type: Boolean,
-      value: false,
-    },
+  static get properties() {
+    return {
+      /**
+       * Contains the OobeTypes.SecurityTokenPinDialogParameters object. It can
+       * be null when our element isn't used.
+       *
+       * Changing this field resets the dialog state. (Please note that, due to
+       * the Polymer's limitation, only assigning a new object is observed;
+       * changing just a subproperty won't work.)
+       */
+      parameters: {
+        type: Object,
+        observer: 'onParametersChanged_',
+      },
 
-    /**
-     * Whether the input is currently non-empty.
-     * @private
-     */
-    hasValue_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * Whether the current state is the wait for the processing completion
+       * (i.e., the backend is verifying the entered PIN).
+       * @private
+       */
+      processingCompletion_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * Whether the user has made changes in the input field since the dialog
-     * was initialized or reset.
-     * @private
-     */
-    userEdited_: {
-      type: Boolean,
-      value: false,
-    },
+      /**
+       * Whether the input is currently non-empty.
+       * @private
+       */
+      hasValue_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * Whether the user can change the value in the input field.
-     * @private
-     */
-    canEdit_: {
-      type: Boolean,
-      computed:
-          'computeCanEdit_(parameters.enableUserInput, processingCompletion_)',
-    },
+      /**
+       * Whether the user has made changes in the input field since the dialog
+       * was initialized or reset.
+       * @private
+       */
+      userEdited_: {
+        type: Boolean,
+        value: false,
+      },
 
-    /**
-     * Whether the user can submit a login request.
-     * @private
-     */
-    canSubmit_: {
-      type: Boolean,
-      computed: 'computeCanSubmit_(parameters.enableUserInput, ' +
-          'hasValue_, processingCompletion_)',
-    },
-  },
+      /**
+       * Whether the user can change the value in the input field.
+       * @private
+       */
+      canEdit_: {
+        type: Boolean,
+        computed:
+            'computeCanEdit_(parameters.enableUserInput, processingCompletion_)',
+      },
+
+      /**
+       * Whether the user can submit a login request.
+       * @private
+       */
+      canSubmit_: {
+        type: Boolean,
+        computed: 'computeCanSubmit_(parameters.enableUserInput, ' +
+            'hasValue_, processingCompletion_)',
+      },
+    };
+  }
 
   focus() {
     // Note: setting the focus synchronously, to avoid flakiness in tests due to
     // racing between the asynchronous caret positioning and the PIN characters
     // input.
     this.$.pinKeyboard.focusInputSynchronously();
-  },
+  }
 
   /**
    * Computes the value of the canEdit_ property.
@@ -94,7 +107,7 @@
    */
   computeCanEdit_(enableUserInput, processingCompletion) {
     return enableUserInput && !processingCompletion;
-  },
+  }
 
   /**
    * Computes the value of the canSubmit_ property.
@@ -106,15 +119,16 @@
    */
   computeCanSubmit_(enableUserInput, hasValue, processingCompletion) {
     return enableUserInput && hasValue && !processingCompletion;
-  },
+  }
 
   /**
    * Invoked when the "Back" button is clicked.
    * @private
    */
   onBackClicked_() {
-    this.fire('cancel');
-  },
+    this.dispatchEvent(
+        new CustomEvent('cancel', {bubbles: true, composed: true}));
+  }
 
   /**
    * Invoked when the "Next" button is clicked or Enter is pressed.
@@ -127,8 +141,10 @@
       return;
     }
     this.processingCompletion_ = true;
-    this.fire('completed', this.$.pinKeyboard.value);
-  },
+    this.dispatchEvent(new CustomEvent(
+        'completed',
+        {bubbles: true, composed: true, detail: this.$.pinKeyboard.value}));
+  }
 
   /**
    * Observer that is called when the |parameters| property gets changed.
@@ -142,7 +158,7 @@
     this.userEdited_ = false;
 
     this.focus();
-  },
+  }
 
   /**
    * Observer that is called when the user changes the PIN input field.
@@ -152,7 +168,7 @@
   onPinChange_(e) {
     this.hasValue_ = e.detail.pin.length > 0;
     this.userEdited_ = true;
-  },
+  }
 
   /**
    * Returns whether the error label should be shown.
@@ -163,7 +179,7 @@
    */
   isErrorLabelVisible_(parameters, userEdited) {
     return parameters && parameters.hasError && !userEdited;
-  },
+  }
 
   /**
    * Returns whether the PIN attempts left count should be shown.
@@ -173,7 +189,7 @@
    */
   isAttemptsLeftVisible_(parameters) {
     return parameters && parameters.formattedAttemptsLeft !== '';
-  },
+  }
 
   /**
    * Returns whether there is a visible label for the PIN input field
@@ -185,7 +201,7 @@
   isLabelVisible_(parameters, userEdited) {
     return this.isErrorLabelVisible_(parameters, userEdited) ||
         this.isAttemptsLeftVisible_(parameters);
-  },
+  }
 
   /**
    * Returns the label to be used for the PIN input field.
@@ -205,6 +221,7 @@
       return parameters.formattedAttemptsLeft;
     }
     return parameters.formattedError;
-  },
-});
-})();
+  }
+}
+
+customElements.define(SecurityTokenPin.is, SecurityTokenPin);
\ No newline at end of file
diff --git a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
index 7e0dffee..1cc69e1 100644
--- a/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
+++ b/chrome/browser/resources/chromeos/login/enterprise_enrollment.html
@@ -204,7 +204,6 @@
         <oobe-text-button id="attributesSkip"
             text-key="oauthEnrollSkip" on-click="skipAttributes_">
         </oobe-text-button>
-        <div class="flex"></div>
         <oobe-next-button id="attributesSubmit"
             on-click="submitAttributes_"></oobe-next-button>
       </div>
diff --git a/chrome/browser/resources/chromeos/login/structure/components_common.html b/chrome/browser/resources/chromeos/login/structure/components_common.html
index b893f6c..ea0bf0e 100644
--- a/chrome/browser/resources/chromeos/login/structure/components_common.html
+++ b/chrome/browser/resources/chromeos/login/structure/components_common.html
@@ -11,6 +11,7 @@
 
 <!-- TODO(crbug.com/1184731) - Move to screen_gaia_signin once migrated -->
 <link rel="import" href="/components/notification_card.html">
+<link rel="import" href="/components/security_token_pin.html">
 
 <link rel="import" href="/components/oobe_a11y_option.html">
 <link rel="import" href="/screens/common/adb_sideloading.html">
@@ -51,7 +52,6 @@
 <include src="../oobe_screen_assistant_optin_flow.html">
 <include src="../multidevice_setup_first_run.html">
 <include src="../screen_multidevice_setup.html">
-<include src="../security_token_pin.html">
 
 <include src="components_[OOBE].html">
 <include src="components_[OS_INSTALL].html">
diff --git a/chrome/browser/resources/chromeos/login/structure/components_common.js b/chrome/browser/resources/chromeos/login/structure/components_common.js
index 2eea2f0..eca4f66a 100644
--- a/chrome/browser/resources/chromeos/login/structure/components_common.js
+++ b/chrome/browser/resources/chromeos/login/structure/components_common.js
@@ -17,7 +17,6 @@
 // <include src="../oobe_screen_assistant_optin_flow.js">
 // <include src="../multidevice_setup_first_run.js">
 // <include src="../screen_multidevice_setup.js">
-// <include src="../security_token_pin.js">
 
 // <include src="components_[OOBE].js">
 // TODO(crbug.com/1111387) - Remove excessive logging.
diff --git a/chrome/browser/resources/settings/chromeos/BUILD.gn b/chrome/browser/resources/settings/chromeos/BUILD.gn
index 96623b07..dc92c58 100644
--- a/chrome/browser/resources/settings/chromeos/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/BUILD.gn
@@ -274,6 +274,7 @@
     "chromeos/os_languages_page/languages_types.js",
     "chromeos/os_languages_page/input_method_util.js",
     "chromeos/os_languages_page/languages_metrics_proxy.js",
+    "chromeos/os_people_page/fingerprint_browser_proxy.js",
     "chromeos/os_printing_page/cups_printers_entry_list_behavior.js",
     "chromeos/os_printing_page/cups_printer_dialog_util.js",
     "chromeos/os_printing_page/cups_printer_types.js",
@@ -497,6 +498,7 @@
     "chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js",
     "chromeos/os_apps_page/app_management_page/pwa_detail_view.js",
     "chromeos/os_apps_page/app_management_page/resize_lock_item.js",
+    "chromeos/os_apps_page/app_management_page/shared_style.js",
     "chromeos/os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.js",
     "chromeos/os_apps_page/app_management_page/supported_links_dialog.js",
     "chromeos/os_apps_page/app_management_page/supported_links_item.js",
@@ -519,21 +521,20 @@
     "chromeos/os_languages_page/shared_vars.js",
     "chromeos/os_languages_page/smart_inputs_page.js",
     "chromeos/os_page_visibility.m.js",
-    "chromeos/os_people_page/account_manager.m.js",
-    "chromeos/os_people_page/fingerprint_browser_proxy.m.js",
-    "chromeos/os_people_page/fingerprint_list.m.js",
-    "chromeos/os_people_page/lock_screen.m.js",
-    "chromeos/os_people_page/lock_screen_password_prompt_dialog.m.js",
+    "chromeos/os_people_page/account_manager.js",
+    "chromeos/os_people_page/fingerprint_list.js",
+    "chromeos/os_people_page/lock_screen.js",
+    "chromeos/os_people_page/lock_screen_password_prompt_dialog.js",
     "chromeos/os_people_page/lock_state_behavior.m.js",
-    "chromeos/os_people_page/os_people_page.m.js",
     "chromeos/os_people_page/os_sync_browser_proxy.m.js",
-    "chromeos/os_people_page/os_sync_controls.m.js",
-    "chromeos/os_people_page/pin_autosubmit_dialog.m.js",
-    "chromeos/os_people_page/setup_fingerprint_dialog.m.js",
-    "chromeos/os_people_page/setup_pin_dialog.m.js",
-    "chromeos/os_people_page/user_list.m.js",
-    "chromeos/os_people_page/users_add_user_dialog.m.js",
-    "chromeos/os_people_page/users_page.m.js",
+    "chromeos/os_people_page/os_people_page.js",
+    "chromeos/os_people_page/os_sync_controls.js",
+    "chromeos/os_people_page/pin_autosubmit_dialog.js",
+    "chromeos/os_people_page/setup_fingerprint_dialog.js",
+    "chromeos/os_people_page/setup_pin_dialog.js",
+    "chromeos/os_people_page/user_list.js",
+    "chromeos/os_people_page/users_add_user_dialog.js",
+    "chromeos/os_people_page/users_page.js",
     "chromeos/os_printing_page/cups_add_print_server_dialog.js",
     "chromeos/os_printing_page/cups_add_printer_dialog.js",
     "chromeos/os_printing_page/cups_add_printer_manually_dialog.js",
@@ -783,6 +784,7 @@
     "os_files_page:web_components",
     "os_languages_page:web_components",
     "os_people_page:polymer3_elements",
+    "os_people_page:web_components",
     "os_printing_page:web_components",
     "os_privacy_page:web_components",
     "os_reset_page:web_components",
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
index 0decdca3..1fa31a7 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_config.js
@@ -17,7 +17,7 @@
 import {HTMLEscape, listenOnce} from '//resources/js/util.m.js';
 import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from '../metrics_recorder.m.js';
+import {recordSettingChange} from '../metrics_recorder.m.js';
 
 Polymer({
   _template: html`{__html_template__}`,
diff --git a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_menu.js b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_menu.js
index 9b7210c..0772e11 100644
--- a/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_menu.js
+++ b/chrome/browser/resources/settings/chromeos/internet_page/internet_detail_menu.js
@@ -17,7 +17,7 @@
 import {OncMojo} from '//resources/cr_components/chromeos/network/onc_mojo.m.js';
 import {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
 import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
-import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {afterNextRender, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
 import {Route, Router} from '../../router.js';
 import {DeepLinkingBehavior} from '../deep_linking_behavior.m.js';
diff --git a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
index 70b5727..75800403 100644
--- a/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/multidevice_page/BUILD.gn
@@ -266,7 +266,7 @@
 js_library("multidevice_screen_lock_subpage.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/multidevice_page/multidevice_screen_lock_subpage.m.js" ]
   deps = [
-    "../os_people_page:lock_screen_password_prompt_dialog.m",
+    "../os_people_page:lock_screen_password_prompt_dialog",
     "../os_people_page:lock_state_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
index a79af07..c69e3c9 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/BUILD.gn
@@ -27,6 +27,7 @@
     ":pwa_detail_view",
     ":reducers",
     ":resize_lock_item",
+    ":shared_style",
     ":store",
     ":store_client",
     ":supported_links_dialog",
@@ -201,6 +202,10 @@
   ]
 }
 
+js_library("shared_style") {
+  deps = [ "//ui/webui/resources/cr_components/app_management:shared_style" ]
+}
+
 js_library("store") {
   deps = [
     "//ui/webui/resources/js:cr.m",
@@ -284,6 +289,7 @@
     "pin_to_shelf_item.js",
     "pwa_detail_view.js",
     "resize_lock_item.js",
+    "shared_style.js",
     "supported_links_overlapping_apps_dialog.js",
     "supported_links_dialog.js",
     "supported_links_item.js",
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
index 2195596c..7708c00 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css cr-icons">
+<style include="app-management-cros-shared-css cr-icons">
   :host {
     align-items: center;
     border-bottom: var(--card-separator);
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js
index bd5f8ec..d06db7b 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/app_item.js
@@ -1,7 +1,7 @@
 // Copyright 2018 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-import '//resources/cr_components/app_management/shared_style.js';
+import './shared_style.js';
 import '//resources/cr_components/app_management/shared_vars.js';
 import '//resources/cr_elements/cr_icons_css.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html
index 386b7de9..38ac7e5 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css">
+<style include="app-management-cros-shared-css">
   #no-permissions {
     border-top: none;
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js
index 00f14d9..4b2ec16 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/arc_detail_view.js
@@ -6,7 +6,7 @@
 import './pin_to_shelf_item.js';
 import './resize_lock_item.js';
 import './supported_links_item.js';
-import '//resources/cr_components/app_management/shared_style.js';
+import './shared_style.js';
 import '//resources/cr_components/app_management/permission_item.js';
 import '//resources/cr_elements/icons.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html
index 5011fcda..84981bc5 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css">
+<style include="app-management-cros-shared-css">
   .permission-text-row {
     border-top: none;
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js
index 5713205..4674f41c 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/borealis_page/borealis_detail_view.js
@@ -8,7 +8,7 @@
 
 import '../icons.js';
 import '../pin_to_shelf_item.js';
-import '//resources/cr_components/app_management/shared_style.js';
+import '../shared_style.js';
 import '//resources/cr_components/app_management/permission_item.js';
 import '//resources/cr_elements/icons.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html
index 4407a39..1f50ea8 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css">
+<style include="app-management-cros-shared-css">
   .permission-text-row:first-of-type {
     border-top: none;
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js
index 8024342a..8a4b91c 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/chrome_app_detail_view.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 import './more_permissions_item.js';
 import './pin_to_shelf_item.js';
-import '//resources/cr_components/app_management/shared_style.js';
+import './shared_style.js';
 
 import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 import {getSelectedApp} from 'chrome://resources/cr_components/app_management/util.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html
index a2fa999..1e1e5d6 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.html
@@ -1,4 +1,4 @@
-<style include="cr-shared-style app-management-shared-css">
+<style include="cr-shared-style app-management-cros-shared-css">
   .app-management-item-arrow {
     margin-inline-end: 8px;
   }
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js
index 4940c7ec..d463476 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/main_view.js
@@ -3,7 +3,7 @@
 // found in the LICENSE file.
 
 import './app_item.js';
-import '//resources/cr_components/app_management/shared_style.js';
+import './shared_style.js';
 import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
 import '//resources/cr_elements/shared_style_css.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.html
index 8889353..9cc72d8 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css">
+<style include="app-management-cros-shared-css">
   :host {
     align-items: center;
     cursor: pointer;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js
index 991544b4..62c533a 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/more_permissions_item.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_components/app_management/shared_style.js';
+import './shared_style.js';
 import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
 
 import {AppManagementUserAction} from '//resources/cr_components/app_management/constants.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
index 4993af8..d038ab7 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css"></style>
+<style include="app-management-cros-shared-css"></style>
 
 <div class="permission-list">
   <app-management-pin-to-shelf-item id="pin-to-shelf-setting"
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js
index 3ea51cd3..3a92aa5d 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js
@@ -5,7 +5,7 @@
 import '../icons.js';
 import '../pin_to_shelf_item.js';
 import '//resources/cr_components/app_management/permission_item.js';
-import '//resources/cr_components/app_management/shared_style.js';
+import '../shared_style.js';
 import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
 import '//resources/cr_elements/icons.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html
index 57bbb0d..f8fcfb930 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css">
+<style include="app-management-cros-shared-css">
 </style>
 <div class="permission-list">
   <app-management-pin-to-shelf-item
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js
index 6ba3ce7..45b476d 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/pwa_detail_view.js
@@ -5,7 +5,7 @@
 import './more_permissions_item.js';
 import './pin_to_shelf_item.js';
 import './supported_links_item.js';
-import '//resources/cr_components/app_management/shared_style.js';
+import './shared_style.js';
 import '//resources/cr_components/app_management/permission_item.js';
 import '//resources/cr_elements/icons.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html
new file mode 100644
index 0000000..77623b0
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.html
@@ -0,0 +1,8 @@
+<template>
+  <style
+      include="cr-shared-style app-management-shared-css cros-color-overrides">
+    .card-container {
+      background-color: var(--cros-bg-color);
+    }
+  </style>
+</template>
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.js
new file mode 100644
index 0000000..fb160eb
--- /dev/null
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/shared_style.js
@@ -0,0 +1,13 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import '//resources/cr_elements/chromeos/cros_color_overrides.m.js';
+import '//resources/cr_components/app_management/shared_style.js';
+
+const template = document.createElement('template');
+template.innerHTML = `
+<dom-module id="app-management-cros-shared-css" assetpath="chrome://resources/">
+{__html_template__}</dom-module>
+`;
+document.body.appendChild(template.content.cloneNode(true));
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_dialog.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_dialog.html
index ae9c15fb..ee98d23 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_dialog.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css settings-shared">
+<style include="app-management-cros-shared-css settings-shared">
   #dialog-body {
     display: flex;
     flex-direction: column;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.html
index 2fdacd9..ac9d32b 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/supported_links_item.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css settings-shared">
+<style include="app-management-cros-shared-css settings-shared">
   #explanation-text {
     align-items: center;
     display: flex;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
index 4a6d42f..0d140d6 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.html
@@ -1,4 +1,4 @@
-<style include="app-management-shared-css">
+<style include="app-management-cros-shared-css">
   :host {
     align-items: center;
     display: flex;
diff --git a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js
index 9215794..f1e66ef 100644
--- a/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js
+++ b/chrome/browser/resources/settings/chromeos/os_apps_page/app_management_page/uninstall_button.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '//resources/cr_components/app_management/shared_style.js';
+import './shared_style.js';
 import '//resources/cr_elements/cr_button/cr_button.m.js';
 import '//resources/cr_elements/policy/cr_tooltip_icon.m.js';
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
index 0add79f..027f0e9 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/BUILD.gn
@@ -3,6 +3,7 @@
 # found in the LICENSE file.
 
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/polymer/html_to_js.gni")
 import("//tools/polymer/polymer.gni")
 import("//ui/webui/resources/tools/js_modulizer.gni")
 import("../os_settings.gni")
@@ -11,21 +12,21 @@
   closure_flags = os_settings_closure_flags
   is_polymer3 = true
   deps = [
-    ":account_manager.m",
+    ":account_manager",
     ":account_manager_browser_proxy",
-    ":fingerprint_browser_proxy.m",
-    ":fingerprint_list.m",
-    ":lock_screen.m",
-    ":lock_screen_password_prompt_dialog.m",
+    ":fingerprint_browser_proxy",
+    ":fingerprint_list",
+    ":lock_screen",
+    ":lock_screen_password_prompt_dialog",
     ":lock_state_behavior.m",
-    ":os_people_page.m",
+    ":os_people_page",
     ":os_sync_browser_proxy.m",
-    ":os_sync_controls.m",
-    ":setup_fingerprint_dialog.m",
-    ":setup_pin_dialog.m",
-    ":user_list.m",
-    ":users_add_user_dialog.m",
-    ":users_page.m",
+    ":os_sync_controls",
+    ":setup_fingerprint_dialog",
+    ":setup_pin_dialog",
+    ":user_list",
+    ":users_add_user_dialog",
+    ":users_page",
   ]
 }
 
@@ -33,8 +34,7 @@
   deps = [ "//ui/webui/resources/js:cr.m" ]
 }
 
-js_library("account_manager.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.m.js" ]
+js_library("account_manager") {
   deps = [
     ":account_manager_browser_proxy",
     "..:deep_linking_behavior.m",
@@ -50,22 +50,18 @@
     "//ui/webui/resources/js:icon",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
-  extra_deps = [ ":account_manager_module" ]
 }
 
-js_library("fingerprint_browser_proxy.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_browser_proxy.m.js" ]
+js_library("fingerprint_browser_proxy") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:cr.m",
   ]
-  extra_deps = [ ":modulize" ]
 }
 
-js_library("fingerprint_list.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.m.js" ]
+js_library("fingerprint_list") {
   deps = [
-    ":fingerprint_browser_proxy.m",
+    ":fingerprint_browser_proxy",
     "..:deep_linking_behavior.m",
     "..:metrics_recorder.m",
     "..:os_route.m",
@@ -80,14 +76,12 @@
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
     "//ui/webui/resources/js/cr/ui:focus_without_ink.m",
   ]
-  extra_deps = [ ":fingerprint_list_module" ]
 }
 
-js_library("lock_screen.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.m.js" ]
+js_library("lock_screen") {
   deps = [
-    ":fingerprint_browser_proxy.m",
-    ":lock_screen_password_prompt_dialog.m",
+    ":fingerprint_browser_proxy",
+    ":lock_screen_password_prompt_dialog",
     ":lock_state_behavior.m",
     "..:deep_linking_behavior.m",
     "..:os_route.m",
@@ -103,18 +97,15 @@
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
     "//ui/webui/resources/js/cr/ui:focus_without_ink.m",
   ]
-  extra_deps = [ ":lock_screen_module" ]
   externs_list = [ "../settings_controls_types.js" ]
 }
 
-js_library("lock_screen_password_prompt_dialog.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.m.js" ]
+js_library("lock_screen_password_prompt_dialog") {
   deps = [
     ":lock_state_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants.m",
   ]
-  extra_deps = [ ":lock_screen_password_prompt_dialog_module" ]
 }
 
 js_library("lock_state_behavior.m") {
@@ -131,13 +122,12 @@
   extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ]
 }
 
-js_library("os_people_page.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.m.js" ]
+js_library("os_people_page") {
   deps = [
     ":account_manager_browser_proxy",
-    ":lock_screen.m",
+    ":lock_screen",
     ":lock_state_behavior.m",
-    ":os_sync_controls.m",
+    ":os_sync_controls",
     "..:deep_linking_behavior.m",
     "..:os_page_visibility.m",
     "..:os_route.m",
@@ -155,7 +145,6 @@
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
     "//ui/webui/resources/js/cr/ui:focus_without_ink.m",
   ]
-  extra_deps = [ ":os_people_page_module" ]
   externs_list =
       chrome_extension_public_externs + [ "../settings_controls_types.js" ]
 }
@@ -170,8 +159,7 @@
   extra_deps = [ ":modulize" ]
 }
 
-js_library("os_sync_controls.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.m.js" ]
+js_library("os_sync_controls") {
   deps = [
     ":os_sync_browser_proxy.m",
     "..:deep_linking_behavior.m",
@@ -185,13 +173,11 @@
     "//ui/webui/resources/js:load_time_data.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
-  extra_deps = [ ":os_sync_controls_module" ]
 }
 
-js_library("setup_fingerprint_dialog.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.m.js" ]
+js_library("setup_fingerprint_dialog") {
   deps = [
-    ":fingerprint_browser_proxy.m",
+    ":fingerprint_browser_proxy",
     "..:metrics_recorder.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_fingerprint:cr_fingerprint_progress_arc.m",
@@ -199,23 +185,19 @@
     "//ui/webui/resources/js:i18n_behavior.m",
     "//ui/webui/resources/js:web_ui_listener_behavior.m",
   ]
-  extra_deps = [ ":setup_fingerprint_dialog_module" ]
 }
 
-js_library("setup_pin_dialog.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.m.js" ]
+js_library("setup_pin_dialog") {
   deps = [
-    ":lock_screen_password_prompt_dialog.m",
+    ":lock_screen_password_prompt_dialog",
     "../..:router",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_components/chromeos/quick_unlock:setup_pin_keyboard.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
-  extra_deps = [ ":setup_pin_dialog_module" ]
 }
 
-js_library("user_list.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/user_list.m.js" ]
+js_library("user_list") {
   deps = [
     "..:os_route.m",
     "..:route_observer_behavior",
@@ -224,30 +206,26 @@
     "//ui/webui/resources/cr_elements:cr_scrollable_behavior.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
-  extra_deps = [ ":user_list_module" ]
   externs_list = [
     "$externs_path/settings_private.js",
     "$externs_path/users_private.js",
   ]
 }
 
-js_library("users_add_user_dialog.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.m.js" ]
+js_library("users_add_user_dialog") {
   deps = [
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/cr_elements/cr_dialog:cr_dialog.m",
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js:i18n_behavior.m",
   ]
-  extra_deps = [ ":users_add_user_dialog_module" ]
   externs_list = [ "$externs_path/users_private.js" ]
 }
 
-js_library("users_page.m") {
-  sources = [ "$root_gen_dir/chrome/browser/resources/settings/chromeos/os_people_page/users_page.m.js" ]
+js_library("users_page") {
   deps = [
-    ":user_list.m",
-    ":users_add_user_dialog.m",
+    ":user_list",
+    ":users_add_user_dialog",
     "..:deep_linking_behavior.m",
     "..:os_route.m",
     "..:route_observer_behavior",
@@ -256,137 +234,31 @@
     "//ui/webui/resources/js:assert.m",
     "//ui/webui/resources/js/cr/ui:focus_without_ink.m",
   ]
-  extra_deps = [ ":users_page_module" ]
 }
 
 group("polymer3_elements") {
-  public_deps = [
-    ":account_manager_module",
-    ":fingerprint_list_module",
-    ":lock_screen_module",
-    ":lock_screen_password_prompt_dialog_module",
-    ":modulize",
-    ":os_people_page_module",
-    ":os_sync_controls_module",
-    ":pin_autosubmit_dialog_module",
-    ":setup_fingerprint_dialog_module",
-    ":setup_pin_dialog_module",
-    ":user_list_module",
-    ":users_add_user_dialog_module",
-    ":users_page_module",
-    "../..:web_components",
+  public_deps = [ ":modulize" ]
+}
+
+html_to_js("web_components") {
+  js_files = [
+    "account_manager.js",
+    "fingerprint_list.js",
+    "lock_screen.js",
+    "lock_screen_password_prompt_dialog.js",
+    "os_people_page.js",
+    "os_sync_controls.js",
+    "setup_fingerprint_dialog.js",
+    "pin_autosubmit_dialog.js",
+    "setup_pin_dialog.js",
+    "user_list.js",
+    "users_add_user_dialog.js",
+    "users_page.js",
   ]
 }
 
-polymer_modulizer("account_manager") {
-  js_file = "account_manager.js"
-  html_file = "account_manager.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("fingerprint_list") {
-  js_file = "fingerprint_list.js"
-  html_file = "fingerprint_list.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("lock_screen") {
-  js_file = "lock_screen.js"
-  html_file = "lock_screen.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("lock_screen_password_prompt_dialog") {
-  js_file = "lock_screen_password_prompt_dialog.js"
-  html_file = "lock_screen_password_prompt_dialog.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("os_people_page") {
-  js_file = "os_people_page.js"
-  html_file = "os_people_page.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("os_sync_controls") {
-  js_file = "os_sync_controls.js"
-  html_file = "os_sync_controls.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("setup_fingerprint_dialog") {
-  js_file = "setup_fingerprint_dialog.js"
-  html_file = "setup_fingerprint_dialog.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("pin_autosubmit_dialog") {
-  js_file = "pin_autosubmit_dialog.js"
-  html_file = "pin_autosubmit_dialog.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-}
-
-polymer_modulizer("setup_pin_dialog") {
-  js_file = "setup_pin_dialog.js"
-  html_file = "setup_pin_dialog.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("user_list") {
-  js_file = "user_list.js"
-  html_file = "user_list.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("users_add_user_dialog") {
-  js_file = "users_add_user_dialog.js"
-  html_file = "users_add_user_dialog.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
-polymer_modulizer("users_page") {
-  js_file = "users_page.js"
-  html_file = "users_page.html"
-  html_type = "dom-module"
-  migrated_imports = os_settings_migrated_imports
-  namespace_rewrites = os_settings_namespace_rewrites
-  auto_imports = os_settings_auto_imports
-}
-
 js_modulizer("modulize") {
   input_files = [
-    "fingerprint_browser_proxy.js",
     "lock_state_behavior.js",
     "os_sync_browser_proxy.js",
   ]
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
index 826b243..6ba6552 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html
@@ -1,412 +1,370 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared iron-flex iron-flex-alignment">
+  :host {
+    --add-account-margin-top: 16px;
+  }
 
-<link rel="import" href="chrome://resources/cr_elements/cr_action_menu/cr_action_menu.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
-<link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/icon.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="chrome://resources/cr_components/chromeos/localized_link/localized_link.html">
-<link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="../metrics_recorder.html">
-<link rel="import" href="../os_route.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../route_observer_behavior.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="account_manager_browser_proxy.html">
-<link rel="import" href="../parental_controls_page/parental_controls_browser_proxy.html">
+  .account-manager-description {
+    color: var(--cr-secondary-text-color);
+    display: block;
+    max-width: 560px;
+  }
 
-<dom-module id="settings-account-manager">
-  <template>
-    <style include="settings-shared iron-flex iron-flex-alignment">
-      :host {
-        --add-account-margin-top: 16px;
-      }
+  .account-manager-description.full-width {
+    max-width: none;
+  }
 
-      .account-manager-description {
-        color: var(--cr-secondary-text-color);
-        display: block;
-        max-width: 560px;
-      }
+  .profile-icon {
+    --profile-icon-size: 40px;
+    background: center / cover no-repeat;
+    border-radius: 50%;
+    flex-shrink: 0;
+    height: var(--profile-icon-size);
+    width: var(--profile-icon-size);
+  }
 
-      .account-manager-description.full-width {
-        max-width: none;
-      }
+  .profile-icon.device-account-icon {
+    --profile-icon-size: 60px;
+    margin-top: 16px;
+  }
 
-      .profile-icon {
-        --profile-icon-size: 40px;
-        background: center / cover no-repeat;
-        border-radius: 50%;
-        flex-shrink: 0;
-        height: var(--profile-icon-size);
-        width: var(--profile-icon-size);
-      }
+  .device-account-container {
+    align-items: center;
+    display: flex;
+    flex-direction: column;
+  }
 
-      .profile-icon.device-account-icon {
-        --profile-icon-size: 60px;
-        margin-top: 16px;
-      }
+  .device-account-container .primary {
+    font-weight: 500;
+    margin-bottom: 4px;
+    margin-top: 16px;
+  }
 
-      .device-account-container {
-        align-items: center;
-        display: flex;
-        flex-direction: column;
-      }
+  .account-list-item {
+    padding-inline-end: 8px;
+    padding-inline-start: 0;
+  }
 
-      .device-account-container .primary {
-        font-weight: 500;
-        margin-bottom: 4px;
-        margin-top: 16px;
-      }
+  #outer {
+    margin-inline-end: var(--cr-section-padding);
+    margin-inline-start: 60px;
+  }
 
-      .account-list-item {
-        padding-inline-end: 8px;
-        padding-inline-start: 0;
-      }
+  .middle .secondary {
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
 
-      #outer {
-        margin-inline-end: var(--cr-section-padding);
-        margin-inline-start: 60px;
-      }
+  .secondary-accounts-policy-indicator {
+    margin-inline-end: 12px;
+  }
 
-      .middle .secondary {
-        overflow: hidden;
-        text-overflow: ellipsis;
-      }
+  .settings-box.user-message {
+    align-items: flex-end;
+  }
 
-      .secondary-accounts-policy-indicator {
-        margin-inline-end: 12px;
-      }
+  .secondary-accounts-tooltip {
+    margin-inline-start: 5px;
+    width: 15px;
+  }
 
-      .settings-box.user-message {
-        align-items: flex-end;
-      }
+  .settings-box.secondary-accounts-box {
+    align-items: flex-end;
+  }
 
-      .secondary-accounts-tooltip {
-        margin-inline-start: 5px;
-        width: 15px;
-      }
+  .account-list-header-description {
+    align-items: center;
+    display: flex;
+    width: 100%;
+  }
 
-      .settings-box.secondary-accounts-box {
-        align-items: flex-end;
-      }
+  .account-list-header-description > .secondary {
+    flex-grow: 1;
+  }
 
-      .account-list-header-description {
-        align-items: center;
-        display: flex;
-        width: 100%;
-      }
+  .account-list-header-description > .secondary {
+    padding-inline-end: 40px;
+  }
 
-      .account-list-header-description > .secondary {
-        flex-grow: 1;
-      }
+  .secondary-accounts-disabled-tooltip {
+    padding-inline-end: 12px;
+  }
 
-      .account-list-header-description > .secondary {
-        padding-inline-end: 40px;
-      }
+  #account-list-header > h2 {
+    padding-bottom: 8px;
+    padding-top: 16px;
+  }
 
-      .secondary-accounts-disabled-tooltip {
-        padding-inline-end: 12px;
-      }
+  #account-list-header {
+    padding-bottom: 8px;
+  }
 
-      #account-list-header > h2 {
-        padding-bottom: 8px;
-        padding-top: 16px;
-      }
+  cr-policy-indicator {
+    margin-inline-end: 1em;
+    margin-top: var(--add-account-margin-top);
+  }
 
-      #account-list-header {
-        padding-bottom: 8px;
-      }
+  .secondary-accounts-box > #add-account-button {
+    margin-bottom: 12px;
+    margin-top: 12px;
+  }
 
-      cr-policy-indicator {
-        margin-inline-end: 1em;
-        margin-top: var(--add-account-margin-top);
-      }
+  #add-account-icon {
+    -webkit-mask-image: url(chrome://resources/images/add.svg);
+    background-color: currentColor;
+    height: 24px;
+    margin-inline-end: 0.5em;
+    width: 24px;
+  }
 
-      .secondary-accounts-box > #add-account-button {
-        margin-bottom: 12px;
-        margin-top: 12px;
-      }
+  .signed-out-text {
+    color: var(--cros-text-color-alert);
+  }
 
-      #add-account-icon {
-        -webkit-mask-image: url(chrome://resources/images/add.svg);
-        background-color: currentColor;
-        height: 24px;
-        margin-inline-end: 0.5em;
-        width: 24px;
-      }
+  .error-badge {
+    background: url(chrome://os-settings/images/error_badge.svg)
+        center / cover no-repeat;
+    display: block;
+    height: 20px;
+    left: 60%;
+    position: relative;
+    top: 60%;
+    width: 20px;
+  }
 
-      .signed-out-text {
-        color: var(--cros-text-color-alert);
-      }
+  @media (prefers-color-scheme: dark) {
+    .error-badge {
+      background: url(chrome://os-settings/images/error_badge_dark.svg)
+          center / cover no-repeat;
+    }
+  }
 
-      .error-badge {
-        background: url(chrome://os-settings/images/error_badge.svg)
-            center / cover no-repeat;
-        display: block;
-        height: 20px;
-        left: 60%;
-        position: relative;
-        top: 60%;
-        width: 20px;
-      }
+  :host-context([dir='rtl']) .error-badge {
+    left: auto;
+    right: 60%;
+  }
 
-      @media (prefers-color-scheme: dark) {
-        .error-badge {
-          background: url(chrome://os-settings/images/error_badge_dark.svg)
-              center / cover no-repeat;
-        }
-      }
+  .managed-badge {
+    --badge-offset: calc(100% - var(--badge-size)
+                         - 2 * var(--padding-size));
+    --badge-size: 10px;
+    --padding-size: 4px;
+    background: var(--cros-icon-color-prominent);
+    border-radius: 50%;
+    height: var(--badge-size);
+    left: var(--badge-offset);
+    padding: var(--padding-size);
+    position: relative;
+    top: var(--badge-offset);
+    width: var(--badge-size);
+  }
 
-      :host-context([dir='rtl']) .error-badge {
-        left: auto;
-        right: 60%;
-      }
+  .managed-badge > iron-icon {
+    --iron-icon-fill-color: var(--cros-bg-color-elevation-1);
+    --iron-icon-height: var(--badge-size);
+    --iron-icon-width: var(--badge-size);
+    display: block;
+  }
 
-      .managed-badge {
-        --badge-offset: calc(100% - var(--badge-size)
-                             - 2 * var(--padding-size));
-        --badge-size: 10px;
-        --padding-size: 4px;
-        background: var(--cros-icon-color-prominent);
-        border-radius: 50%;
-        height: var(--badge-size);
-        left: var(--badge-offset);
-        padding: var(--padding-size);
-        position: relative;
-        top: var(--badge-offset);
-        width: var(--badge-size);
-      }
+  :host-context([dir='rtl']) .managed-badge {
+    left: auto;
+    right: var(--badge-offset);
+  }
 
-      .managed-badge > iron-icon {
-        --iron-icon-fill-color: var(--cros-bg-color-elevation-1);
-        --iron-icon-height: var(--badge-size);
-        --iron-icon-width: var(--badge-size);
-        display: block;
-      }
+  .managed-message {
+    color: var(--cr-secondary-text-color);
+    justify-content: center;
+    margin-top: 16px;
+  }
 
-      :host-context([dir='rtl']) .managed-badge {
-        left: auto;
-        right: var(--badge-offset);
-      }
+  .managed-message > iron-icon,
+  .managed-message > cr-icon-button {
+    margin-inline-end: 5px;
+  }
 
-      .managed-message {
-        color: var(--cr-secondary-text-color);
-        justify-content: center;
-        margin-top: 16px;
-      }
+  .management-status {
+    color: var(--cr-secondary-text-color);
+    overflow: hidden;
+    text-align: end;
+    text-overflow: ellipsis;
+    width: 18ch;
+  }
 
-      .managed-message > iron-icon,
-      .managed-message > cr-icon-button {
-        margin-inline-end: 5px;
-      }
+  .edu-account-label {
+    margin-inline-start: 12px;
+  }
 
-      .management-status {
-        color: var(--cr-secondary-text-color);
-        overflow: hidden;
-        text-align: end;
-        text-overflow: ellipsis;
-        width: 18ch;
-      }
+  .tooltip-primary-account {
+    margin-inline-end: 12px;
+    margin-inline-start: 12px;
+  }
 
-      .edu-account-label {
-        margin-inline-start: 12px;
-      }
+  .no-accounts-message {
+    color: var(--cr-secondary-text-color);
+    padding: 20px 50px;
+    text-align: center;
+  }
 
-      .tooltip-primary-account {
-        margin-inline-end: 12px;
-        margin-inline-start: 12px;
-      }
+  .settings-box.border-bottom {
+    border-bottom: var(--cr-separator-line);
+  }
 
-      .no-accounts-message {
-        color: var(--cr-secondary-text-color);
-        padding: 20px 50px;
-        text-align: center;
-      }
-
-      .settings-box.border-bottom {
-        border-bottom: var(--cr-separator-line);
-      }
-
-      #removeConfirmationButton {
-        --active-shadow-action-rgb: var(--google-red-refresh-500-rgb);
-        --bg-action: var(--google-red-600);
-        --focus-shadow-color: rgba(var(--google-red-600-rgb), .4);
-        --hover-bg-action: rgba(var(--google-red-600-rgb), .9);
-        --hover-shadow-action-rgb: var(--google-red-refresh-500-rgb);
-        --hover-border-color: var(--google-red-100);
-        --hover-shadow-action-rgb: var(--google-red-refresh-500-rgb);
-      }
-
-    </style>
-
-    <!-- Account management description -->
-    <div class="settings-box first account-manager-description full-width">
-      <localized-link
-          localized-string="[[getAccountManagerDescription_()]]"
-          link-url="$i18nRaw{accountManagerLearnMoreUrl}">
-      </localized-link>
-    </div>
-
-    <!-- Managed account badge and message -->
-    <template is="dom-if" if="[[isDeviceAccountManaged_]]">
-      <div class="settings-box first managed-message">
-        <template is="dom-if" if="[[!isChildUser_]]">
-          <iron-icon icon="cr20:domain"></iron-icon>
-        </template>
-        <template is="dom-if" if="[[isChildUser_]]">
-          <cr-icon-button iron-icon="cr20:kite"
-              on-click="onManagedIconClick_">
-          </cr-icon-button>
-        </template>
-        <localized-link
-            localized-string=
-              "[[getManagementDescription_(isChildUser_, deviceAccount_)]]"
-            link-url="$i18nRaw{accountManagerChromeUIManagementURL}">
-        </localized-link>
+  #removeConfirmationButton {
+    --active-shadow-action-rgb: var(--google-red-refresh-500-rgb);
+    --bg-action: var(--google-red-600);
+    --focus-shadow-color: rgba(var(--google-red-600-rgb), .4);
+    --hover-bg-action: rgba(var(--google-red-600-rgb), .9);
+    --hover-shadow-action-rgb: var(--google-red-refresh-500-rgb);
+    --hover-border-color: var(--google-red-100);
+    --hover-shadow-action-rgb: var(--google-red-refresh-500-rgb);
+  }
+</style>
+<!-- Account management description -->
+<div class="settings-box first account-manager-description full-width">
+  <localized-link
+      localized-string="[[getAccountManagerDescription_()]]"
+      link-url="$i18nRaw{accountManagerLearnMoreUrl}">
+  </localized-link>
+</div>
+<!-- Managed account badge and message -->
+<template is="dom-if" if="[[isDeviceAccountManaged_]]">
+  <div class="settings-box first managed-message">
+    <template is="dom-if" if="[[!isChildUser_]]">
+      <iron-icon icon="cr20:domain"></iron-icon>
+    </template>
+    <template is="dom-if" if="[[isChildUser_]]">
+      <cr-icon-button iron-icon="cr20:kite"
+          on-click="onManagedIconClick_">
+      </cr-icon-button>
+    </template>
+    <localized-link
+        localized-string=
+          "[[getManagementDescription_(isChildUser_, deviceAccount_)]]"
+        link-url="$i18nRaw{accountManagerChromeUIManagementURL}">
+    </localized-link>
+  </div>
+</template>
+<!-- Primary account icon, name and email -->
+<div class="device-account-container"
+    aria-labelledby="deviceAccountFullName"
+    aria-describedby="deviceAccountEmail">
+  <div class="profile-icon device-account-icon"
+      aria-hidden="true"
+      style="background-image: [[getIconImageSet_(deviceAccount_.pic)]]">
+    <template is="dom-if"
+        if="[[shouldShowManagedBadge_(isDeviceAccountManaged_,
+              isChildUser_)]]">
+      <div class="managed-badge">
+        <iron-icon icon="cr:work"></iron-icon>
       </div>
     </template>
-
-    <!-- Primary account icon, name and email -->
-    <div class="device-account-container"
-        aria-labelledby="deviceAccountFullName"
-        aria-describedby="deviceAccountEmail">
-      <div class="profile-icon device-account-icon"
-          aria-hidden="true"
-          style="background-image: [[getIconImageSet_(deviceAccount_.pic)]]">
-        <template is="dom-if"
-            if="[[shouldShowManagedBadge_(isDeviceAccountManaged_,
-                  isChildUser_)]]">
-          <div class="managed-badge">
-            <iron-icon icon="cr:work"></iron-icon>
-          </div>
+  </div>
+  <span id="deviceAccountFullName" class="primary" aria-hidden="true">
+    [[deviceAccount_.fullName]]
+  </span>
+  <span id="deviceAccountEmail" class="secondary" aria-hidden="true">
+    [[deviceAccount_.email]]
+  </span>
+</div>
+<!-- Secondary Accounts list header -->
+<div class="secondary-accounts-box settings-box first">
+  <div id="account-list-header" class="start">
+    <h2>
+      [[getAccountListHeader_(isChildUser_)]]
+    </h2>
+    <div class="account-list-header-description">
+      <span class="secondary">
+        [[getAccountListDescription_(isChildUser_)]]
+      </span>
+      <template is="dom-if" if="[[
+                              !isSecondaryGoogleAccountSigninAllowed_]]">
+        <cr-tooltip-icon
+            class="secondary-accounts-disabled-tooltip"
+            icon-class="[[getManagedAccountTooltipIcon_(isChildUser_)]]"
+            tooltip-text="[[getSecondaryAccountsDisabledUserMessage_(
+                                isChildUser_)]]"
+            icon-aria-label="[[getSecondaryAccountsDisabledUserMessage_(
+                                  isChildUser_)]]">
+        </cr-tooltip-icon>
+      </template>
+      <cr-button disabled="[[!isSecondaryGoogleAccountSigninAllowed_]]"
+          id="add-account-button" on-click="addAccount_"
+          deep-link-focus-id$="[[Setting.kAddAccount]]">
+        <div id="add-account-icon"></div>
+        [[getAddAccountLabel_(isChildUser_,
+            isSecondaryGoogleAccountSigninAllowed_)]]
+      </cr-button>
+    </div>
+  </div>
+</div>
+<!-- Secondary Accounts list -->
+<div id="outer" class="layout vertical nowrap" role="list">
+  <template is="dom-repeat" id="account-list"
+      items="[[getSecondaryAccounts_(accounts_)]]">
+    <div class="settings-box account-list-item" role="listitem">
+      <div class="profile-icon"
+          style="background-image: [[getIconImageSet_(item.pic)]]">
+        <template is="dom-if" if="[[!item.isSignedIn]]">
+          <span class="error-badge"></span>
         </template>
       </div>
-      <span id="deviceAccountFullName" class="primary" aria-hidden="true">
-        [[deviceAccount_.fullName]]
-      </span>
-      <span id="deviceAccountEmail" class="secondary" aria-hidden="true">
-        [[deviceAccount_.email]]
-      </span>
-    </div>
-
-    <!-- Secondary Accounts list header -->
-    <div class="secondary-accounts-box settings-box first">
-      <div id="account-list-header" class="start">
-        <h2>
-          [[getAccountListHeader_(isChildUser_)]]
-        </h2>
-        <div class="account-list-header-description">
-          <span class="secondary">
-            [[getAccountListDescription_(isChildUser_)]]
-          </span>
-          <template is="dom-if" if="[[
-                                  !isSecondaryGoogleAccountSigninAllowed_]]">
-            <cr-tooltip-icon
-                class="secondary-accounts-disabled-tooltip"
-                icon-class="[[getManagedAccountTooltipIcon_(isChildUser_)]]"
-                tooltip-text="[[getSecondaryAccountsDisabledUserMessage_(
-                                    isChildUser_)]]"
-                icon-aria-label="[[getSecondaryAccountsDisabledUserMessage_(
-                                      isChildUser_)]]">
-            </cr-tooltip-icon>
+      <div class="middle two-line no-min-width">
+        <div class="flex text-elide">
+          <!-- If account is signed in, display the full name -->
+          <template is="dom-if" if="[[item.isSignedIn]]">
+            <span id="fullName-[[index]]"
+                aria-hidden="true">[[item.fullName]]</span>
           </template>
-          <cr-button disabled="[[!isSecondaryGoogleAccountSigninAllowed_]]"
-              id="add-account-button" on-click="addAccount_"
-              deep-link-focus-id$="[[Setting.kAddAccount]]">
-            <div id="add-account-icon"></div>
-            [[getAddAccountLabel_(isChildUser_,
-                isSecondaryGoogleAccountSigninAllowed_)]]
-          </cr-button>
+          <!-- Else, display a re-authentication message -->
+          <template is="dom-if" if="[[!item.isSignedIn]]">
+            <span class="signed-out-text">
+              [[getAccountManagerSignedOutName_(item.unmigrated)]]
+            </span>
+          </template>
+          <div class="secondary" id="email-[[index]]"
+              aria-hidden="true">[[item.email]]</div>
         </div>
       </div>
-    </div>
-
-    <!-- Secondary Accounts list -->
-    <div id="outer" class="layout vertical nowrap" role="list">
-      <template is="dom-repeat" id="account-list"
-          items="[[getSecondaryAccounts_(accounts_)]]">
-        <div class="settings-box account-list-item" role="listitem">
-
-          <div class="profile-icon"
-              style="background-image: [[getIconImageSet_(item.pic)]]">
-            <template is="dom-if" if="[[!item.isSignedIn]]">
-              <span class="error-badge"></span>
-            </template>
-          </div>
-
-          <div class="middle two-line no-min-width">
-            <div class="flex text-elide">
-              <!-- If account is signed in, display the full name -->
-              <template is="dom-if" if="[[item.isSignedIn]]">
-                <span id="fullName-[[index]]"
-                    aria-hidden="true">[[item.fullName]]</span>
-              </template>
-              <!-- Else, display a re-authentication message -->
-              <template is="dom-if" if="[[!item.isSignedIn]]">
-                <span class="signed-out-text">
-                  [[getAccountManagerSignedOutName_(item.unmigrated)]]
-                </span>
-              </template>
-
-              <div class="secondary" id="email-[[index]]"
-                  aria-hidden="true">[[item.email]]</div>
-            </div>
-          </div>
-
-          <template is="dom-if"
-              if="[[shouldShowReauthenticationButton_(item)]]">
-            <cr-button title="[[getAccountManagerSignedOutTitle_(item)]]"
-                class="reauth-button" on-click="onReauthenticationTap_"
-                aria-labelledby$="fullName-[[index]] email-[[index]]">
-              [[getAccountManagerSignedOutLabel_(item.unmigrated)]]
-            </cr-button>
-          </template>
-
-          <!-- Display a hamburger menu for removing the account -->
-          <cr-icon-button class="icon-more-vert"
-              title="[[getMoreActionsTitle_(item)]]"
-              aria-label="[[getMoreActionsTitle_(item)]]"
-              aria-describedby$="fullName-[[index]]
-                                  edu-account-label-[[index]]"
-              on-click="onAccountActionsMenuButtonTap_"
-              deep-link-focus-id$="[[Setting.kRemoveAccount]]">
-          </cr-icon-button>
-        </div>
+      <template is="dom-if"
+          if="[[shouldShowReauthenticationButton_(item)]]">
+        <cr-button title="[[getAccountManagerSignedOutTitle_(item)]]"
+            class="reauth-button" on-click="onReauthenticationTap_"
+            aria-labelledby$="fullName-[[index]] email-[[index]]">
+          [[getAccountManagerSignedOutLabel_(item.unmigrated)]]
+        </cr-button>
       </template>
-
-      <cr-action-menu role-description="$i18n{menu}">
-        <button class="dropdown-item" on-click="onRemoveAccountTap_">
-          $i18n{removeAccountLabel}
-        </button>
-      </cr-action-menu>
+      <!-- Display a hamburger menu for removing the account -->
+      <cr-icon-button class="icon-more-vert"
+          title="[[getMoreActionsTitle_(item)]]"
+          aria-label="[[getMoreActionsTitle_(item)]]"
+          aria-describedby$="fullName-[[index]]
+                              edu-account-label-[[index]]"
+          on-click="onAccountActionsMenuButtonTap_"
+          deep-link-focus-id$="[[Setting.kRemoveAccount]]">
+      </cr-icon-button>
     </div>
-
-    <div class="settings-box"></div>
-
-    <cr-dialog id="removeConfirmationDialog">
-      <div slot="title" class="key-text">
-        $i18n{removeLacrosAccountDialogTitle}
-      </div>
-      <div slot="body" class="warning-message">
-        $i18n{removeLacrosAccountDialogBody}
-      </div>
-      <div slot="button-container">
-        <cr-button class="cancel-button"
-            on-click="onRemoveAccountDialogCancelTap_">
-          $i18n{removeLacrosAccountDialogCancel}
-        </cr-button>
-        <cr-button id="removeConfirmationButton" class="action-button"
-            on-click="onRemoveAccountDialogRemoveTap_">
-          $i18n{removeLacrosAccountDialogRemove}
-        </cr-button>
-      </div>
-    </cr-dialog>
-
   </template>
-  <script src="account_manager.js"></script>
-</dom-module>
+  <cr-action-menu role-description="$i18n{menu}">
+    <button class="dropdown-item" on-click="onRemoveAccountTap_">
+      $i18n{removeAccountLabel}
+    </button>
+  </cr-action-menu>
+</div>
+<div class="settings-box"></div>
+<cr-dialog id="removeConfirmationDialog">
+  <div slot="title" class="key-text">
+    $i18n{removeLacrosAccountDialogTitle}
+  </div>
+  <div slot="body" class="warning-message">
+    $i18n{removeLacrosAccountDialogBody}
+  </div>
+  <div slot="button-container">
+    <cr-button class="cancel-button"
+        on-click="onRemoveAccountDialogCancelTap_">
+      $i18n{removeLacrosAccountDialogCancel}
+    </cr-button>
+    <cr-button id="removeConfirmationButton" class="action-button"
+        on-click="onRemoveAccountDialogRemoveTap_">
+      $i18n{removeLacrosAccountDialogRemove}
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
index 327f7fd..37d8b566 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/account_manager.js
@@ -8,20 +8,45 @@
  * list, add and delete Secondary Google Accounts.
  */
 
+import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import '//resources/cr_elements/policy/cr_policy_indicator.m.js';
+import '//resources/cr_elements/policy/cr_tooltip_icon.m.js';
+import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import '//resources/cr_components/chromeos/localized_link/localized_link.js';
+import '../../settings_shared_css.js';
+
+import {CrActionMenuElement} from '//resources/cr_elements/cr_action_menu/cr_action_menu.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {getImage} from '//resources/js/icon.js';
+import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
+import {html, Polymer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {loadTimeData} from '../../i18n_setup.js';
+import {Route, Router} from '../../router.js';
+import {DeepLinkingBehavior} from '../deep_linking_behavior.m.js';
+import {recordSettingChange} from '../metrics_recorder.m.js';
+import {routes} from '../os_route.m.js';
+import {ParentalControlsBrowserProxy, ParentalControlsBrowserProxyImpl} from '../parental_controls_page/parental_controls_browser_proxy.js';
+import {RouteObserverBehavior} from '../route_observer_behavior.js';
+
+import {Account, AccountManagerBrowserProxy, AccountManagerBrowserProxyImpl} from './account_manager_browser_proxy.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-account-manager',
 
   behaviors: [
     DeepLinkingBehavior,
     I18nBehavior,
     WebUIListenerBehavior,
-    settings.RouteObserverBehavior,
+    RouteObserverBehavior,
   ],
 
   properties: {
     /**
      * List of Accounts.
-     * @type {!Array<settings.Account>}
+     * @type {!Array<Account>}
      */
     accounts_: {
       type: Array,
@@ -32,13 +57,13 @@
 
     /**
      * Primary / Device account.
-     * @private {?settings.Account}
+     * @private {?Account}
      */
     deviceAccount_: Object,
 
     /**
      * The targeted account for menu operations.
-     * @private {?settings.Account}
+     * @private {?Account}
      */
     actionMenuAccount_: Object,
 
@@ -87,7 +112,7 @@
     },
   },
 
-  /** @private {?settings.AccountManagerBrowserProxy} */
+  /** @private {?AccountManagerBrowserProxy} */
   browserProxy_: null,
 
   /** @override */
@@ -97,16 +122,16 @@
 
   /** @override */
   ready() {
-    this.browserProxy_ = settings.AccountManagerBrowserProxyImpl.getInstance();
+    this.browserProxy_ = AccountManagerBrowserProxyImpl.getInstance();
     this.refreshAccounts_();
   },
 
   /**
-   * @param {!settings.Route} newRoute
-   * @param {settings.Route} oldRoute
+   * @param {!Route} newRoute
+   * @param {Route} oldRoute
    */
   currentRouteChanged(newRoute, oldRoute) {
-    if (newRoute !== settings.routes.ACCOUNT_MANAGER) {
+    if (newRoute !== routes.ACCOUNT_MANAGER) {
       return;
     }
 
@@ -174,7 +199,7 @@
    * @private
    */
   getIconImageSet_(iconUrl) {
-    return cr.icon.getImage(iconUrl);
+    return getImage(iconUrl);
   },
 
   /**
@@ -182,14 +207,14 @@
    * @private
    */
   addAccount_(event) {
-    settings.recordSettingChange(
+    recordSettingChange(
         chromeos.settings.mojom.Setting.kAddAccount,
         {intValue: this.accounts_.length + 1});
     this.browserProxy_.addAccount();
   },
 
   /**
-   * @param {!settings.Account} account
+   * @param {!Account} account
    * @return {boolean} True if the account reauthentication button should be
    *    shown, false otherwise.
    * @private
@@ -271,7 +296,7 @@
 
 
   /**
-   * @param {!settings.Account} account
+   * @param {!Account} account
    * @private
    */
   getAccountManagerSignedOutTitle_(account) {
@@ -281,7 +306,7 @@
   },
 
   /**
-   * @param {!settings.Account} account
+   * @param {!Account} account
    * @private
    */
   getMoreActionsTitle_(account) {
@@ -290,7 +315,7 @@
   },
 
   /**
-   * @return {!Array<settings.Account>} list of accounts.
+   * @return {!Array<Account>} list of accounts.
    * @private
    */
   getSecondaryAccounts_() {
@@ -298,7 +323,7 @@
   },
 
   /**
-   * @param {!CustomEvent<!{model: !{item: !settings.Account}}>} event
+   * @param {!CustomEvent<!{model: !{item: !Account}}>} event
    * @private
    */
   onReauthenticationTap_(event) {
@@ -312,8 +337,7 @@
   /** @private */
   onManagedIconClick_() {
     if (this.isChildUser_) {
-      parental_controls.ParentalControlsBrowserProxyImpl.getInstance()
-          .launchFamilyLinkSettings();
+      ParentalControlsBrowserProxyImpl.getInstance().launchFamilyLinkSettings();
     }
   },
 
@@ -334,7 +358,7 @@
 
   /**
    * Opens the Account actions menu.
-   * @param {!{model: !{item: settings.Account}, target: !Element}} event
+   * @param {!{model: !{item: Account}, target: !Element}} event
    * @private
    */
   onAccountActionsMenuButtonTap_(event) {
@@ -356,7 +380,7 @@
       this.$.removeConfirmationDialog.showModal();
     } else {
       this.browserProxy_.removeAccount(
-          /** @type {?settings.Account} */ (this.actionMenuAccount_));
+          /** @type {?Account} */ (this.actionMenuAccount_));
       this.actionMenuAccount_ = null;
       this.$$('#add-account-button').focus();
     }
@@ -380,7 +404,7 @@
    */
   onRemoveAccountDialogRemoveTap_() {
     this.browserProxy_.removeAccount(
-        /** @type {?settings.Account} */ (this.actionMenuAccount_));
+        /** @type {?Account} */ (this.actionMenuAccount_));
     this.actionMenuAccount_ = null;
     this.$.removeConfirmationDialog.close();
     this.$$('#add-account-button').focus();
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_browser_proxy.js b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_browser_proxy.js
index 97b276f8..4f01901 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_browser_proxy.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_browser_proxy.js
@@ -3,170 +3,158 @@
 // found in the LICENSE file.
 
 // clang-format off
-// #import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
+import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 // clang-format on
 
-cr.define('settings', function() {
   /**
    * @enum {number}
    * These values must be kept in sync with the values in
    * third_party/cros_system_api/dbus/service_constants.h.
    */
-  /* #export */ const FingerprintResultType = {
-    SUCCESS: 0,
-    PARTIAL: 1,
-    INSUFFICIENT: 2,
-    SENSOR_DIRTY: 3,
-    TOO_SLOW: 4,
-    TOO_FAST: 5,
-    IMMOBILE: 6,
-  };
+export const FingerprintResultType = {
+  SUCCESS: 0,
+  PARTIAL: 1,
+  INSUFFICIENT: 2,
+  SENSOR_DIRTY: 3,
+  TOO_SLOW: 4,
+  TOO_FAST: 5,
+  IMMOBILE: 6,
+};
+
+/**
+ * An object describing a attempt from the fingerprint hardware. The structure
+ * of this data must be kept in sync with C++ FingerprintHandler.
+ * @typedef {{
+ *   result: FingerprintResultType,
+ *   indexes: !Array<number>,
+ * }}
+ */
+export let FingerprintAttempt;
+
+/**
+ * An object describing a scan from the fingerprint hardware. The structure of
+ * this data must be kept in sync with C++ FingerprintHandler.
+ * @typedef {{
+ *   result: FingerprintResultType,
+ *   isComplete: boolean,
+ *   percentComplete: number,
+ * }}
+ */
+export let FingerprintScan;
+
+/**
+ * An object describing the necessary info to display on the fingerprint
+ * settings. The structure of this data must be kept in sync with
+ * C++ FingerprintHandler.
+ * @typedef {{
+ *   fingerprintsList: !Array<string>,
+ *   isMaxed: boolean,
+ * }}
+ */
+export let FingerprintInfo;
+
+/** @interface */
+export class FingerprintBrowserProxy {
+  /**
+   * @return {!Promise<!FingerprintInfo>}
+   */
+  getFingerprintsList() {}
 
   /**
-   * An object describing a attempt from the fingerprint hardware. The structure
-   * of this data must be kept in sync with C++ FingerprintHandler.
-   * @typedef {{
-   *   result: settings.FingerprintResultType,
-   *   indexes: !Array<number>,
-   * }}
+   * @return {!Promise<number>}
    */
-  /* #export */ let FingerprintAttempt;
+  getNumFingerprints() {}
 
   /**
-   * An object describing a scan from the fingerprint hardware. The structure of
-   * this data must be kept in sync with C++ FingerprintHandler.
-   * @typedef {{
-   *   result: settings.FingerprintResultType,
-   *   isComplete: boolean,
-   *   percentComplete: number,
-   * }}
+   * @param {string} authToken
    */
-  /* #export */ let FingerprintScan;
+  startEnroll(authToken) {}
+
+  cancelCurrentEnroll() {}
 
   /**
-   * An object describing the necessary info to display on the fingerprint
-   * settings. The structure of this data must be kept in sync with
-   * C++ FingerprintHandler.
-   * @typedef {{
-   *   fingerprintsList: !Array<string>,
-   *   isMaxed: boolean,
-   * }}
+   * @param {number} index
+   * @return {!Promise<string>}
    */
-  /* #export */ let FingerprintInfo;
+  getEnrollmentLabel(index) {}
 
-  /** @interface */
-  /* #export */ class FingerprintBrowserProxy {
-    /**
-     * @return {!Promise<!settings.FingerprintInfo>}
-     */
-    getFingerprintsList() {}
+  /**
+   * @param {number} index
+   * @return {!Promise<boolean>}
+   */
+  removeEnrollment(index) {}
 
-    /**
-     * @return {!Promise<number>}
-     */
-    getNumFingerprints() {}
+  /**
+   * @param {number} index
+   * @param {string} newLabel
+   * @return {!Promise<boolean>}
+   */
+  changeEnrollmentLabel(index, newLabel) {}
 
-    /**
-     * @param {string} authToken
-     */
-    startEnroll(authToken) {}
+  startAuthentication() {}
+  endCurrentAuthentication() {}
 
-    cancelCurrentEnroll() {}
+  /**
+   * TODO(sammiequon): Temporary function to let the handler know when a
+   * completed scan has been sent via click on the setup fingerprint dialog.
+   * Remove this when real scans are implemented.
+   */
+  fakeScanComplete() {}
+}
 
-    /**
-     * @param {number} index
-     * @return {!Promise<string>}
-     */
-    getEnrollmentLabel(index) {}
-
-    /**
-     * @param {number} index
-     * @return {!Promise<boolean>}
-     */
-    removeEnrollment(index) {}
-
-    /**
-     * @param {number} index
-     * @param {string} newLabel
-     * @return {!Promise<boolean>}
-     */
-    changeEnrollmentLabel(index, newLabel) {}
-
-    startAuthentication() {}
-    endCurrentAuthentication() {}
-
-    /**
-     * TODO(sammiequon): Temporary function to let the handler know when a
-     * completed scan has been sent via click on the setup fingerprint dialog.
-     * Remove this when real scans are implemented.
-     */
-    fakeScanComplete() {}
+/**
+ * @implements {FingerprintBrowserProxy}
+ */
+export class FingerprintBrowserProxyImpl {
+  /** @override */
+  getFingerprintsList() {
+    return sendWithPromise('getFingerprintsList');
   }
 
-  /**
-   * @implements {settings.FingerprintBrowserProxy}
-   */
-  /* #export */ class FingerprintBrowserProxyImpl {
-    /** @override */
-    getFingerprintsList() {
-      return cr.sendWithPromise('getFingerprintsList');
-    }
-
-    /** @override */
-    getNumFingerprints() {
-      return cr.sendWithPromise('getNumFingerprints');
-    }
-
-    /** @override */
-    startEnroll(authToken) {
-      chrome.send('startEnroll', [authToken]);
-    }
-
-    /** @override */
-    cancelCurrentEnroll() {
-      chrome.send('cancelCurrentEnroll');
-    }
-
-    /** @override */
-    getEnrollmentLabel(index) {
-      return cr.sendWithPromise('getEnrollmentLabel');
-    }
-
-    /** @override */
-    removeEnrollment(index) {
-      return cr.sendWithPromise('removeEnrollment', index);
-    }
-
-    /** @override */
-    changeEnrollmentLabel(index, newLabel) {
-      return cr.sendWithPromise('changeEnrollmentLabel', index, newLabel);
-    }
-
-    /** @override */
-    startAuthentication() {
-      chrome.send('startAuthentication');
-    }
-
-    /** @override */
-    endCurrentAuthentication() {
-      chrome.send('endCurrentAuthentication');
-    }
-
-    /** @override */
-    fakeScanComplete() {
-      chrome.send('fakeScanComplete');
-    }
+  /** @override */
+  getNumFingerprints() {
+    return sendWithPromise('getNumFingerprints');
   }
 
-  cr.addSingletonGetter(FingerprintBrowserProxyImpl);
+  /** @override */
+  startEnroll(authToken) {
+    chrome.send('startEnroll', [authToken]);
+  }
 
-  // #cr_define_end
-  return {
-    FingerprintAttempt,
-    FingerprintBrowserProxy,
-    FingerprintBrowserProxyImpl,
-    FingerprintInfo,
-    FingerprintResultType,
-    FingerprintScan,
-  };
-});
+  /** @override */
+  cancelCurrentEnroll() {
+    chrome.send('cancelCurrentEnroll');
+  }
+
+  /** @override */
+  getEnrollmentLabel(index) {
+    return sendWithPromise('getEnrollmentLabel');
+  }
+
+  /** @override */
+  removeEnrollment(index) {
+    return sendWithPromise('removeEnrollment', index);
+  }
+
+  /** @override */
+  changeEnrollmentLabel(index, newLabel) {
+    return sendWithPromise('changeEnrollmentLabel', index, newLabel);
+  }
+
+  /** @override */
+  startAuthentication() {
+    chrome.send('startAuthentication');
+  }
+
+  /** @override */
+  endCurrentAuthentication() {
+    chrome.send('endCurrentAuthentication');
+  }
+
+  /** @override */
+  fakeScanComplete() {
+    chrome.send('fakeScanComplete');
+  }
+}
+
+addSingletonGetter(FingerprintBrowserProxyImpl);
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.html b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.html
index 8d1d919e..22b83f1 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.html
@@ -1,121 +1,87 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared">
+  .add-link {
+    background-color: transparent;
+    color: var(--cros-link-color);
+  }
 
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/policy/cr_tooltip_icon.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-list/iron-list.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/paper-ripple/paper-ripple.html">
-<link rel="import" href="fingerprint_browser_proxy.html">
-<link rel="import" href="setup_fingerprint_dialog.html">
-<link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="chrome://resources/cr_components/chromeos/localized_link/localized_link.html">
-<link rel="import" href="../os_route.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../route_observer_behavior.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="../metrics_recorder.html">
+  .add-link[disabled] {
+    color: var(--cros-text-color-disabled);
+  }
 
-<dom-module id="settings-fingerprint-list">
-  <template>
-    <style include="settings-shared">
-      .add-link {
-        background-color: transparent;
-        color: var(--cros-link-color);
-      }
+  .list-item {
+    background-color: var(--cros-bg-color-elevation-1);
+  }
 
-      .add-link[disabled] {
-        color: var(--cros-text-color-disabled);
-      }
+  cr-input {
+    --cr-input-error-display: none;
+  }
 
-      .list-item {
-        background-color: var(--cros-bg-color-elevation-1);
-      }
+  #fingerprintTooltip {
+    --paper-tooltip-animation: {
+      box-shadow: var(--cr-card-shadow);
+    }
+    --paper-tooltip-background: var(--cros-tooltip-background-color);
+    --paper-tooltip-border-radius: 8px;
+    --paper-tooltip-delay-out: 500ms;
+    --paper-tooltip-opacity: 1;
+    --paper-tooltip-padding: 12px 16px;
+    --paper-tooltip-text-color: var(--cros-tooltip-label-color);
+  }
 
-      cr-input {
-        --cr-input-error-display: none;
-      }
+  #fingerprintNotice {
+    --cr-localized-link-display: block;
+    font-size: 12px;
+    line-height: 18px;
+  }
 
-      #fingerprintTooltip {
-        --paper-tooltip-animation: {
-          box-shadow: var(--cr-card-shadow);
-        }
-
-        --paper-tooltip-background: var(--cros-tooltip-background-color);
-        --paper-tooltip-border-radius: 8px;
-        --paper-tooltip-delay-out: 500ms;
-        --paper-tooltip-opacity: 1;
-        --paper-tooltip-padding: 12px 16px;
-        --paper-tooltip-text-color: var(--cros-tooltip-label-color);
-      }
-
-      #fingerprintNotice {
-        --cr-localized-link-display: block;
-
-        font-size: 12px;
-        line-height: 18px;
-      }
-
-      #fingerprintWarning {
-        font-style: italic;
-      }
-    </style>
-
-    <h2 class="settings-box">$i18n{lockScreenRegisteredFingerprints}
-      <cr-tooltip-icon id="fingerprintTooltip" tooltip-position="right"
-          icon-class="cr:help-outline" icon-aria-label="$i18n{learnMore}">
-        <localized-link id="fingerprintNotice"
-            localized-string="$i18n{lockScreenFingerprintNotice}"
-            link-url="$i18n{fingerprintLearnMoreLink}"
-            slot="tooltip-text">
-          <!-- paper-tooltip probes for children textContent to decide whether
-            to show tooltip or not-->
-          _
-        </localized-link>
-      </cr-tooltip-icon>
-    </h2>
-    <div class="list-frame layout vertical">
-      <iron-list id="fingerprintsList" items="[[fingerprints_]]">
-        <template>
-          <div class="list-item">
-            <cr-input value="{{item}}" on-change="onFingerprintLabelChanged_">
-            </cr-input>
-            <cr-icon-button class="icon-delete-gray"
-                on-click="onFingerprintDeleteTapped_"
-                aria-label$="[[getButtonAriaLabel_(item)]]"
-                deep-link-focus-id$="[[Setting.kRemoveFingerprintV2]]">
-            </cr-icon-button>
-          </div>
-        </template>
-      </iron-list>
-      <div class="continuation">
-        <cr-button id="addFingerprint" class="add-link action-button"
-            on-click="openAddFingerprintDialog_"
-            deep-link-focus-id$="[[Setting.kAddFingerprintV2]]">
-          $i18n{lockScreenAddFingerprint}
-        </cr-button>
+  #fingerprintWarning {
+    font-style: italic;
+  }
+</style>
+<h2 class="settings-box">$i18n{lockScreenRegisteredFingerprints}
+  <cr-tooltip-icon id="fingerprintTooltip" tooltip-position="right"
+      icon-class="cr:help-outline" icon-aria-label="$i18n{learnMore}">
+    <localized-link id="fingerprintNotice"
+        localized-string="$i18n{lockScreenFingerprintNotice}"
+        link-url="$i18n{fingerprintLearnMoreLink}"
+        slot="tooltip-text">
+      <!-- paper-tooltip probes for children textContent to decide whether
+        to show tooltip or not-->
+      _
+    </localized-link>
+  </cr-tooltip-icon>
+</h2>
+<div class="list-frame layout vertical">
+  <iron-list id="fingerprintsList" items="[[fingerprints_]]">
+    <template>
+      <div class="list-item">
+        <cr-input value="{{item}}" on-change="onFingerprintLabelChanged_">
+        </cr-input>
+        <cr-icon-button class="icon-delete-gray"
+            on-click="onFingerprintDeleteTapped_"
+            aria-label$="[[getButtonAriaLabel_(item)]]"
+            deep-link-focus-id$="[[Setting.kRemoveFingerprintV2]]">
+        </cr-icon-button>
       </div>
-    </div>
-    <div id="fingerprintWarning" class="settings-box continuation">
-      <iron-icon icon="cr:info-outline"></iron-icon>
-      $i18n{lockScreenFingerprintWarning}
-    </div>
-
-    <template is="dom-if" if="[[showSetupFingerprintDialog_]]" restamp>
-      <settings-setup-fingerprint-dialog
-          auth-token="[[authToken]]"
-          on-add-fingerprint="updateFingerprintsList_"
-          on-close="onSetupFingerprintDialogClose_"
-          allow-add-another-finger="[[allowAddAnotherFinger_]]">
-      </settings-setup-fingerprint-dialog>
     </template>
-  </template>
-  <script src="fingerprint_list.js"></script>
-</dom-module>
+  </iron-list>
+  <div class="continuation">
+    <cr-button id="addFingerprint" class="add-link action-button"
+        on-click="openAddFingerprintDialog_"
+        deep-link-focus-id$="[[Setting.kAddFingerprintV2]]">
+      $i18n{lockScreenAddFingerprint}
+    </cr-button>
+  </div>
+</div>
+<div id="fingerprintWarning" class="settings-box continuation">
+  <iron-icon icon="cr:info-outline"></iron-icon>
+  $i18n{lockScreenFingerprintWarning}
+</div>
+<template is="dom-if" if="[[showSetupFingerprintDialog_]]" restamp>
+  <settings-setup-fingerprint-dialog
+      auth-token="[[authToken]]"
+      on-add-fingerprint="updateFingerprintsList_"
+      on-close="onSetupFingerprintDialogClose_"
+      allow-add-another-finger="[[allowAddAnotherFinger_]]">
+  </settings-setup-fingerprint-dialog>
+</template>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js
index 191543d..6308c0d 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.js
@@ -9,14 +9,40 @@
  */
 const FLASH_DURATION_MS = 500;
 
+import {afterNextRender, Polymer, html, flush, Templatizer, TemplateInstanceBase} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import '//resources/cr_elements/cr_input/cr_input.m.js';
+import '//resources/cr_elements/icons.m.js';
+import '//resources/cr_elements/policy/cr_tooltip_icon.m.js';
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {focusWithoutInk} from '//resources/js/cr/ui/focus_without_ink.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '//resources/polymer/v3_0/iron-list/iron-list.js';
+import '//resources/polymer/v3_0/paper-ripple/paper-ripple.js';
+import {FingerprintInfo, FingerprintBrowserProxy, FingerprintResultType, FingerprintBrowserProxyImpl} from './fingerprint_browser_proxy.js';
+import './setup_fingerprint_dialog.js';
+import {loadTimeData} from '../../i18n_setup.js';
+import {DeepLinkingBehavior} from '../deep_linking_behavior.m.js';
+import '//resources/cr_components/chromeos/localized_link/localized_link.js';
+import {routes} from '../os_route.m.js';
+import {Router, Route} from '../../router.js';
+import {RouteObserverBehavior} from '../route_observer_behavior.js';
+import '../../settings_shared_css.js';
+import {recordSettingChange} from '../metrics_recorder.m.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-fingerprint-list',
 
   behaviors: [
     DeepLinkingBehavior,
     I18nBehavior,
     WebUIListenerBehavior,
-    settings.RouteObserverBehavior,
+    RouteObserverBehavior,
   ],
 
   properties: {
@@ -66,13 +92,13 @@
     },
   },
 
-  /** @private {?settings.FingerprintBrowserProxy} */
+  /** @private {?FingerprintBrowserProxy} */
   browserProxy_: null,
 
   /** @override */
   attached() {
     this.addWebUIListener('on-screen-locked', this.onScreenLocked_.bind(this));
-    this.browserProxy_ = settings.FingerprintBrowserProxyImpl.getInstance();
+    this.browserProxy_ = FingerprintBrowserProxyImpl.getInstance();
     this.browserProxy_.startAuthentication();
     this.updateFingerprintsList_();
   },
@@ -87,8 +113,8 @@
    * @private
    */
   requestPasswordIfApplicable_() {
-    const currentRoute = settings.Router.getInstance().getCurrentRoute();
-    if (currentRoute === settings.routes.FINGERPRINT && !this.authToken) {
+    const currentRoute = Router.getInstance().getCurrentRoute();
+    if (currentRoute === routes.FINGERPRINT && !this.authToken) {
       this.fire('password-requested');
       return true;
     }
@@ -96,13 +122,13 @@
   },
 
   /**
-   * Overridden from settings.RouteObserverBehavior.
-   * @param {!settings.Route} newRoute
-   * @param {!settings.Route} oldRoute
+   * Overridden from RouteObserverBehavior.
+   * @param {!Route} newRoute
+   * @param {!Route} oldRoute
    * @protected
    */
   currentRouteChanged(newRoute, oldRoute) {
-    if (newRoute !== settings.routes.FINGERPRINT) {
+    if (newRoute !== routes.FINGERPRINT) {
       if (this.browserProxy_) {
         this.browserProxy_.endCurrentAuthentication();
       }
@@ -110,7 +136,7 @@
       return;
     }
 
-    if (oldRoute === settings.routes.LOCK_SCREEN) {
+    if (oldRoute === routes.LOCK_SCREEN) {
       // Start fingerprint authentication when going from LOCK_SCREEN to
       // FINGERPRINT page.
       this.browserProxy_.startAuthentication();
@@ -130,7 +156,7 @@
   },
 
   /**
-   * @param {!settings.FingerprintInfo} fingerprintInfo
+   * @param {!FingerprintInfo} fingerprintInfo
    * @private
    */
   onFingerprintsChanged_(fingerprintInfo) {
@@ -148,7 +174,7 @@
   onFingerprintDeleteTapped_(e) {
     this.browserProxy_.removeEnrollment(e.model.index).then(success => {
       if (success) {
-        settings.recordSettingChange();
+        recordSettingChange();
         this.updateFingerprintsList_();
       }
     });
@@ -178,7 +204,7 @@
   /** @private */
   onSetupFingerprintDialogClose_() {
     this.showSetupFingerprintDialog_ = false;
-    cr.ui.focusWithoutInk(assert(this.$$('#addFingerprint')));
+    focusWithoutInk(assert(this.$$('#addFingerprint')));
     this.browserProxy_.startAuthentication();
   },
 
@@ -189,8 +215,7 @@
    */
   onScreenLocked_(screenIsLocked) {
     if (!screenIsLocked &&
-        settings.Router.getInstance().getCurrentRoute() ===
-            settings.routes.FINGERPRINT) {
+        Router.getInstance().getCurrentRoute() === routes.FINGERPRINT) {
       this.onSetupFingerprintDialogClose_();
     }
   },
@@ -202,8 +227,7 @@
       return;
     }
 
-    if (settings.Router.getInstance().getCurrentRoute() ===
-        settings.routes.FINGERPRINT) {
+    if (Router.getInstance().getCurrentRoute() === routes.FINGERPRINT) {
       // Show deep links again if the user authentication dialog just closed.
       this.attemptDeepLink();
     }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.html b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.html
index 60d2f39..66e53fc 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.html
@@ -1,212 +1,171 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared">
+  cr-policy-indicator {
+    margin-inline-start: auto;
+    /* Align the indicator with the h2 that it is associated with. */
+    padding-bottom: 12px;
+    padding-top: 24px;
+  }
 
-<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_button/cr_radio_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_radio_group/cr_radio_group.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/plural_string_proxy.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="../../controls/settings_toggle_button.html">
-<link rel="import" href="fingerprint_browser_proxy.html">
-<link rel="import" href="lock_state_behavior.html">
-<link rel="import" href="setup_pin_dialog.html">
-<link rel="import" href="pin_autosubmit_dialog.html">
-<link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../prefs_behavior.html">
-<link rel="import" href="../../prefs/prefs.html">
-<link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="../os_route.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../route_observer_behavior.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="../../settings_vars_css.html">
-<link rel="import" href="../multidevice_page/multidevice_smartlock_item.html">
+  cr-radio-group {
+    display: flex;
+    flex-wrap: wrap;
+  }
 
-<dom-module id="settings-lock-screen">
-  <template>
-    <style include="settings-shared">
-      cr-policy-indicator {
-        margin-inline-start: auto;
-        /* Align the indicator with the h2 that it is associated with. */
-        padding-bottom: 12px;
-        padding-top: 24px;
-      }
+  .list-item {
+    width: 100%;
+  }
 
-      cr-radio-group {
-        display: flex;
-        flex-wrap: wrap;
-      }
+  .list-item-end {
+    align-items: center;
+    display: flex;
+  }
 
-      .list-item {
-        width: 100%;
-      }
+  .list-item-start {
+    flex: 1;
+  }
 
-      .list-item-end {
-        align-items: center;
-        display: flex;
-      }
+  #lockOptionsDiv {
+    display: block;
+  }
 
-      .list-item-start {
-        flex: 1;
-      }
+  #pinPasswordLabel,
+  #pinPasswordSecondaryActionDiv {
+    margin: auto;
+  }
 
-      #lockOptionsDiv {
-        display: block;
-      }
+  .underbar {
+    border-bottom: var(--cr-separator-line);
+  }
 
-      #pinPasswordLabel,
-      #pinPasswordSecondaryActionDiv {
-        margin: auto;
-      }
+  #unlockType[disabled] {
+    opacity: var(--settings-disabled-opacity);
+    pointer-events: none;
+  }
 
-      .underbar {
-        border-bottom: var(--cr-separator-line);
-      }
-
-      #unlockType[disabled] {
-        opacity: var(--settings-disabled-opacity);
-        pointer-events: none;
-      }
-
-      #autosubmitToggle {
-        padding-inline-start: 16px;
-        width: 100%;
-      }
-    </style>
-
-    <div>
-      <settings-toggle-button id="enableLockScreen"
-          pref="{{prefs.settings.enable_screen_lock}}"
-          on-settings-boolean-control-change="onScreenLockChange_" no-set-pref
-          label="$i18n{enableScreenlock}"
-          deep-link-focus-id$="[[Setting.kLockScreenV2]]">
-      </settings-toggle-button>
-
-      <template is="dom-if" if="[[quickUnlockEnabled_]]">
-        <div id="lockOptionsDiv">
-          <div class="settings-box">
-            <h2 id=lockScreenOptionsTitle aria-hidden="true">
-              [[selectLockScreenOptionsString(hasPinLogin)]]
-            </h2>
-            <template is="dom-if" if="[[quickUnlockDisabledByPolicy_]]">
-              <cr-policy-indicator indicator-type="userPolicy">
-              </cr-policy-indicator>
-            </template>
-          </div>
-          <div class="list-frame" >
-            <cr-radio-group id="unlockType"
-                disabled$="[[quickUnlockDisabledByPolicy_]]"
-                selected="{{selectedUnlockType}}"
-                aria-labelledby="lockScreenOptionsTitle"
-                deep-link-focus-id$="[[Setting.kChangeAuthPinV2]]">
-              <cr-radio-button name="password" class="list-item underbar"
-                  label=$i18n{lockScreenPasswordOnly}>
-              </cr-radio-button>
-              <cr-radio-button name="pin+password" class="list-item-start"
-                  label=$i18n{lockScreenPinOrPassword}>
-              </cr-radio-button>
-              <template is="dom-if"
-                  if="[[showConfigurePinButton_(selectedUnlockType)]]">
-                <div class="list-item-end">
-                  <div class="separator"></div>
-                  <div id="pinPasswordSecondaryActionDiv"
-                      class="secondary-action">
-                    <!-- Use stop-keyboard-event-propagation to prevent
-                         triggering this when focused after closing the
-                         dialog. -->
-                    <cr-button id="setupPinButton" on-click="onConfigurePin_"
-                        stop-keyboard-event-propagation>
-                      [[getSetupPinText_(hasPin)]]
-                    </cr-button>
-                  </div>
-                </div>
-              </template>
-              <template is="dom-if"
-                  if="[[quickUnlockPinAutosubmitFeatureEnabled_]]">
-                <div id="autosubmitToggle"
-                    hidden="[[!hasPin]]">
-                  <settings-toggle-button id="enablePinAutoSubmit"
-                      pref="{{prefs.pin_unlock_autosubmit_enabled}}" no-set-pref
-                      on-settings-boolean-control-change="onPinAutosubmitChange_"
-                      label="$i18n{lockScreenPinAutoSubmit}">
-                  </settings-toggle-button>
-                </div>
-              </template>
-            </cr-radio-group>
-          </div>
-        </div>
-      </template>
-
-      <template is="dom-if" if="[[lockScreenNotificationsEnabled_]]">
-        <h2 class="settings-box">
-          $i18n{lockScreenNotificationTitle}
+  #autosubmitToggle {
+    padding-inline-start: 16px;
+    width: 100%;
+  }
+</style>
+<div>
+  <settings-toggle-button id="enableLockScreen"
+      pref="{{prefs.settings.enable_screen_lock}}"
+      on-settings-boolean-control-change="onScreenLockChange_" no-set-pref
+      label="$i18n{enableScreenlock}"
+      deep-link-focus-id$="[[Setting.kLockScreenV2]]">
+  </settings-toggle-button>
+  <template is="dom-if" if="[[quickUnlockEnabled_]]">
+    <div id="lockOptionsDiv">
+      <div class="settings-box">
+        <h2 id=lockScreenOptionsTitle aria-hidden="true">
+          [[selectLockScreenOptionsString(hasPinLogin)]]
         </h2>
-        <div class="list-frame">
-          <settings-radio-group
-              pref="{{prefs.ash.message_center.lock_screen_mode}}"
-              group-aria-label="$i18n{lockScreenNotificationTitle}">
-            <template is="dom-if"
-                if="[[lockScreenHideSensitiveNotificationSupported_]]">
-              <cr-radio-button name="hideSensitive" class="list-item underbar"
-                   pref="[[prefs.ash.message_center.lock_screen_mode]]"
-                   label="$i18n{lockScreenNotificationHideSensitive}">
-              </cr-radio-button>
-            </template>
-            <cr-radio-button name="show" class="list-item underbar"
-                 pref="[[prefs.ash.message_center.lock_screen_mode]]"
-                 label="$i18n{lockScreenNotificationShow}">
-            </cr-radio-button>
-            <cr-radio-button name="hide" class="list-item"
-                 pref="[[prefs.ash.message_center.lock_screen_mode]]"
-                 label="$i18n{lockScreenNotificationHide}">
-            </cr-radio-button>
-          </settings-radio-group>
-        </div>
-      </template>
-
-      <template is="dom-if" if="[[fingerprintUnlockEnabled_]]">
-        <div id="fingerprintDiv" class="settings-box two-line">
-          <div class="start">
-            $i18n{lockScreenEditFingerprints}
-            <div class="secondary" id="lockScreenEditFingerprintsSecondary">
-              [[numFingerprintDescription_]]
+        <template is="dom-if" if="[[quickUnlockDisabledByPolicy_]]">
+          <cr-policy-indicator indicator-type="userPolicy">
+          </cr-policy-indicator>
+        </template>
+      </div>
+      <div class="list-frame" >
+        <cr-radio-group id="unlockType"
+            disabled$="[[quickUnlockDisabledByPolicy_]]"
+            selected="{{selectedUnlockType}}"
+            aria-labelledby="lockScreenOptionsTitle"
+            deep-link-focus-id$="[[Setting.kChangeAuthPinV2]]">
+          <cr-radio-button name="password" class="list-item underbar"
+              label=$i18n{lockScreenPasswordOnly}>
+          </cr-radio-button>
+          <cr-radio-button name="pin+password" class="list-item-start"
+              label=$i18n{lockScreenPinOrPassword}>
+          </cr-radio-button>
+          <template is="dom-if"
+              if="[[showConfigurePinButton_(selectedUnlockType)]]">
+            <div class="list-item-end">
+              <div class="separator"></div>
+              <div id="pinPasswordSecondaryActionDiv"
+                  class="secondary-action">
+                <!-- Use stop-keyboard-event-propagation to prevent
+                     triggering this when focused after closing the
+                     dialog. -->
+                <cr-button id="setupPinButton" on-click="onConfigurePin_"
+                    stop-keyboard-event-propagation>
+                  [[getSetupPinText_(hasPin)]]
+                </cr-button>
+              </div>
             </div>
-          </div>
-          <div class="separator"></div>
-          <div class="secondary-action">
-            <cr-button id="editFingerprints" on-click="onEditFingerprints_"
-                aria-label="$i18n{lockScreenEditFingerprints}"
-                aria-descibedby="lockScreenEditFingerprintsSecondary">
-              $i18n{lockScreenSetupFingerprintButton}
-            </cr-button>
-          </div>
-        </div>
-      </template>
-
-      <template is="dom-if" if="[[smartLockUIRevampEnabled_]]">
-        <settings-multidevice-smartlock-item
-          auth-token="[[authToken]]">
-        </settings-multidevice-smartlock-item>
-      </template>
-
-      <template is="dom-if" if="[[showSetupPinDialog_]]" restamp>
-        <settings-setup-pin-dialog id="setupPin" set-modes="[[setModes]]"
-            on-close="onSetupPinDialogClose_">
-        </settings-setup-pin-dialog>
-      </template>
-
-      <template is="dom-if" if="[[showPinAutosubmitDialog_]]" restamp>
-        <settings-pin-autosubmit-dialog id="pinAutosubmitDialog"
-            auth-token="[[authToken]]"
-            on-close="onPinAutosubmitDialogClose_">
-        </settings-pin-autosubmit-dialog>
-      </template>
+          </template>
+          <template is="dom-if"
+              if="[[quickUnlockPinAutosubmitFeatureEnabled_]]">
+            <div id="autosubmitToggle"
+                hidden="[[!hasPin]]">
+              <settings-toggle-button id="enablePinAutoSubmit"
+                  pref="{{prefs.pin_unlock_autosubmit_enabled}}" no-set-pref
+                  on-settings-boolean-control-change="onPinAutosubmitChange_"
+                  label="$i18n{lockScreenPinAutoSubmit}">
+              </settings-toggle-button>
+            </div>
+          </template>
+        </cr-radio-group>
+      </div>
     </div>
   </template>
-  <script src="lock_screen.js"></script>
-</dom-module>
+  <template is="dom-if" if="[[lockScreenNotificationsEnabled_]]">
+    <h2 class="settings-box">
+      $i18n{lockScreenNotificationTitle}
+    </h2>
+    <div class="list-frame">
+      <settings-radio-group
+          pref="{{prefs.ash.message_center.lock_screen_mode}}"
+          group-aria-label="$i18n{lockScreenNotificationTitle}">
+        <template is="dom-if"
+            if="[[lockScreenHideSensitiveNotificationSupported_]]">
+          <cr-radio-button name="hideSensitive" class="list-item underbar"
+               pref="[[prefs.ash.message_center.lock_screen_mode]]"
+               label="$i18n{lockScreenNotificationHideSensitive}">
+          </cr-radio-button>
+        </template>
+        <cr-radio-button name="show" class="list-item underbar"
+             pref="[[prefs.ash.message_center.lock_screen_mode]]"
+             label="$i18n{lockScreenNotificationShow}">
+        </cr-radio-button>
+        <cr-radio-button name="hide" class="list-item"
+             pref="[[prefs.ash.message_center.lock_screen_mode]]"
+             label="$i18n{lockScreenNotificationHide}">
+        </cr-radio-button>
+      </settings-radio-group>
+    </div>
+  </template>
+  <template is="dom-if" if="[[fingerprintUnlockEnabled_]]">
+    <div id="fingerprintDiv" class="settings-box two-line">
+      <div class="start">
+        $i18n{lockScreenEditFingerprints}
+        <div class="secondary" id="lockScreenEditFingerprintsSecondary">
+          [[numFingerprintDescription_]]
+        </div>
+      </div>
+      <div class="separator"></div>
+      <div class="secondary-action">
+        <cr-button id="editFingerprints" on-click="onEditFingerprints_"
+            aria-label="$i18n{lockScreenEditFingerprints}"
+            aria-descibedby="lockScreenEditFingerprintsSecondary">
+          $i18n{lockScreenSetupFingerprintButton}
+        </cr-button>
+      </div>
+    </div>
+  </template>
+  <template is="dom-if" if="[[smartLockUIRevampEnabled_]]">
+    <settings-multidevice-smartlock-item
+      auth-token="[[authToken]]">
+    </settings-multidevice-smartlock-item>
+  </template>
+  <template is="dom-if" if="[[showSetupPinDialog_]]" restamp>
+    <settings-setup-pin-dialog id="setupPin" set-modes="[[setModes]]"
+        on-close="onSetupPinDialogClose_">
+    </settings-setup-pin-dialog>
+  </template>
+  <template is="dom-if" if="[[showPinAutosubmitDialog_]]" restamp>
+    <settings-pin-autosubmit-dialog id="pinAutosubmitDialog"
+        auth-token="[[authToken]]"
+        on-close="onPinAutosubmitDialogClose_">
+    </settings-pin-autosubmit-dialog>
+  </template>
+</div>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
index 29c2bf2..bed881a 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen.js
@@ -14,7 +14,39 @@
  * </settings-lock-screen>
  */
 
+import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
+import '//resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
+import '//resources/cr_elements/shared_vars_css.m.js';
+import '//resources/cr_elements/policy/cr_policy_indicator.m.js';
+import '../../controls/settings_toggle_button.js';
+import './setup_pin_dialog.js';
+import './pin_autosubmit_dialog.js';
+import '../../prefs/prefs.js';
+import '../../settings_shared_css.js';
+import '../../settings_vars_css.js';
+import '../multidevice_page/multidevice_smartlock_item.m.js';
+
+import {LockScreenProgress, recordLockScreenProgress} from '//resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {focusWithoutInk} from '//resources/js/cr/ui/focus_without_ink.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {PluralStringProxyImpl} from '//resources/js/plural_string_proxy.js';
+import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {loadTimeData} from '../../i18n_setup.js';
+import {Route, Router} from '../../router.js';
+import {DeepLinkingBehavior} from '../deep_linking_behavior.m.js';
+import {routes} from '../os_route.m.js';
+import {PrefsBehavior} from '../prefs_behavior.js';
+import {RouteObserverBehavior} from '../route_observer_behavior.js';
+
+import {FingerprintAttempt, FingerprintBrowserProxy, FingerprintBrowserProxyImpl, FingerprintInfo, FingerprintResultType, FingerprintScan} from './fingerprint_browser_proxy.js';
+import {LockScreenUnlockType, LockStateBehavior, LockStateBehaviorImpl} from './lock_state_behavior.m.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-lock-screen',
 
   behaviors: [
@@ -22,7 +54,7 @@
     I18nBehavior,
     LockStateBehavior,
     WebUIListenerBehavior,
-    settings.RouteObserverBehavior,
+    RouteObserverBehavior,
   ],
 
   properties: {
@@ -58,7 +90,7 @@
     writeUma_: {
       type: Object,
       value() {
-        return settings.recordLockScreenProgress;
+        return recordLockScreenProgress;
       },
     },
 
@@ -177,7 +209,7 @@
     },
   },
 
-  /** @private {?settings.FingerprintBrowserProxy} */
+  /** @private {?FingerprintBrowserProxy} */
   fingerprintBrowserProxy_: null,
 
   /** selectedUnlockType is defined in LockStateBehavior. */
@@ -185,8 +217,7 @@
 
   /** @override */
   attached() {
-    this.fingerprintBrowserProxy_ =
-        settings.FingerprintBrowserProxyImpl.getInstance();
+    this.fingerprintBrowserProxy_ = FingerprintBrowserProxyImpl.getInstance();
     this.updateNumFingerprints_();
 
     this.addWebUIListener(
@@ -198,13 +229,13 @@
   },
 
   /**
-   * Overridden from settings.RouteObserverBehavior.
-   * @param {!settings.Route} newRoute
-   * @param {!settings.Route} oldRoute
+   * Overridden from RouteObserverBehavior.
+   * @param {!Route} newRoute
+   * @param {!Route} oldRoute
    * @protected
    */
   currentRouteChanged(newRoute, oldRoute) {
-    if (newRoute === settings.routes.LOCK_SCREEN) {
+    if (newRoute === routes.LOCK_SCREEN) {
       this.updateUnlockType(/*activeModesChanged=*/ false);
       this.updateNumFingerprints_();
       this.attemptDeepLink();
@@ -288,11 +319,11 @@
 
   /** @private */
   focusDefaultElement_() {
-    Polymer.RenderStatus.afterNextRender(this, () => {
+    afterNextRender(this, () => {
       if (!this.$$('#unlockType').disabled) {
-        cr.ui.focusWithoutInk(assert(this.$$('#unlockType')));
+        focusWithoutInk(assert(this.$$('#unlockType')));
       } else {
-        cr.ui.focusWithoutInk(assert(this.$$('#enableLockScreen')));
+        focusWithoutInk(assert(this.$$('#enableLockScreen')));
       }
     });
   },
@@ -305,8 +336,7 @@
       return;
     }
 
-    if (settings.Router.getInstance().getCurrentRoute() ===
-        settings.routes.LOCK_SCREEN) {
+    if (Router.getInstance().getCurrentRoute() === routes.LOCK_SCREEN) {
       // Show deep links again if the user authentication dialog just closed.
       this.attemptDeepLink().then(result => {
         // If there were no supported deep links, focus the default element.
@@ -323,20 +353,20 @@
    */
   onConfigurePin_(e) {
     e.preventDefault();
-    this.writeUma_(settings.LockScreenProgress.CHOOSE_PIN_OR_PASSWORD);
+    this.writeUma_(LockScreenProgress.CHOOSE_PIN_OR_PASSWORD);
     this.showSetupPinDialog_ = true;
   },
 
   /** @private */
   onSetupPinDialogClose_() {
     this.showSetupPinDialog_ = false;
-    cr.ui.focusWithoutInk(assert(this.$$('#setupPinButton')));
+    focusWithoutInk(assert(this.$$('#setupPinButton')));
   },
 
   /** @private */
   onPinAutosubmitDialogClose_() {
     this.showPinAutosubmitDialog_ = false;
-    cr.ui.focusWithoutInk(assert(this.$$('#enablePinAutoSubmit')));
+    focusWithoutInk(assert(this.$$('#enablePinAutoSubmit')));
   },
 
   /**
@@ -375,7 +405,7 @@
 
   /** @private */
   onEditFingerprints_() {
-    settings.Router.getInstance().navigateTo(settings.routes.FINGERPRINT);
+    Router.getInstance().navigateTo(routes.FINGERPRINT);
   },
 
   /**
@@ -383,8 +413,8 @@
    * @private
    */
   requestPasswordIfApplicable_() {
-    const currentRoute = settings.Router.getInstance().getCurrentRoute();
-    if (currentRoute === settings.routes.LOCK_SCREEN && !this.setModes) {
+    const currentRoute = Router.getInstance().getCurrentRoute();
+    if (currentRoute === routes.LOCK_SCREEN && !this.setModes) {
       this.fire('password-requested');
       return true;
     }
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.html b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.html
index 8108d3f..302815c 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.html
@@ -1,16 +1,5 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html">
-<link rel="import" href="../../controls/password_prompt_dialog.html">
-<link rel="import" href="lock_state_behavior.html">
-
-<dom-module id="settings-lock-screen-password-prompt-dialog">
-  <template>
-    <settings-password-prompt-dialog
-        id="passwordPrompt"
-        password-prompt-text="[[selectPasswordPromptEnterPasswordString_(hasPinLogin)]]"
-        on-token-obtained="onTokenObtained_">
-    </settings-password-prompt-dialog>
-  </template>
-  <script src="lock_screen_password_prompt_dialog.js"></script>
-</dom-module>
+<settings-password-prompt-dialog
+    id="passwordPrompt"
+    password-prompt-text="[[selectPasswordPromptEnterPasswordString_(hasPinLogin)]]"
+    on-token-obtained="onTokenObtained_">
+</settings-password-prompt-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
index c460a0d..13bd1f4f 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.js
@@ -16,7 +16,15 @@
  *   id="lockScreenPasswordPrompt"
  * </settings-lock-screen-password-prompt-dialog>
  */
+import '../../controls/password_prompt_dialog.js';
+
+import {LockScreenProgress, recordLockScreenProgress} from '//resources/cr_components/chromeos/quick_unlock/lock_screen_constants.m.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {LockScreenUnlockType, LockStateBehavior, LockStateBehaviorImpl} from './lock_state_behavior.m.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-lock-screen-password-prompt-dialog',
 
   behaviors: [
@@ -34,14 +42,14 @@
     writeUma_: {
       type: Object,
       value() {
-        return settings.recordLockScreenProgress;
+        return recordLockScreenProgress;
       }
     },
   },
 
   /** @override */
   attached() {
-    this.writeUma_(settings.LockScreenProgress.START_SCREEN_LOCK);
+    this.writeUma_(LockScreenProgress.START_SCREEN_LOCK);
   },
 
   /**
@@ -50,7 +58,7 @@
    */
   onTokenObtained_(e) {
     // The user successfully authenticated.
-    this.writeUma_(settings.LockScreenProgress.ENTER_PASSWORD_CORRECTLY);
+    this.writeUma_(LockScreenProgress.ENTER_PASSWORD_CORRECTLY);
     this.fire('auth-token-obtained', e.detail);
   },
 
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
index f136a67..e425fbc8 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html
@@ -1,187 +1,132 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared iron-flex">
+  :host {
+    --icon-width: 40px;
+  }
 
-<link rel="import" href="chrome://resources/cr_elements/chromeos/cr_picture/png.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_link_row/cr_link_row.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="chrome://resources/cr_elements/policy/cr_policy_indicator.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/icon.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="../../controls/settings_toggle_button.html">
-<link rel="import" href="account_manager_browser_proxy.html">
-<link rel="import" href="../../people_page/profile_info_browser_proxy.html">
-<link rel="import" href="../../people_page/signout_dialog.html">
-<link rel="import" href="../../people_page/sync_browser_proxy.html">
-<link rel="import" href="../../people_page/sync_controls.html">
-<link rel="import" href="../../people_page/sync_page.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../route_observer_behavior.html">
-<link rel="import" href="../../settings_page/settings_animated_pages.html">
-<link rel="import" href="../../settings_page/settings_subpage.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="../os_route.html">
-<link rel="import" href="../os_page_visibility.html">
-<link rel="import" href="../parental_controls_page/parental_controls_page.html">
-<link rel="import" href="account_manager.html">
-<link rel="import" href="fingerprint_list.html">
-<link rel="import" href="lock_screen.html">
-<link rel="import" href="lock_screen_password_prompt_dialog.html">
-<link rel="import" href="lock_state_behavior.html">
-<link rel="import" href="users_page.html">
-<link rel="import" href="os_sync_controls.html">
+  .sync-row {
+    align-items: center;
+    flex: auto;
+  }
 
-<dom-module id="os-settings-people-page">
-  <template>
-    <style include="settings-shared iron-flex">
-      :host {
-        --icon-width: 40px;
-      }
+  #profile-icon {
+    background: center / cover no-repeat;
+    border-radius: 20px;
+    flex-shrink: 0;
+    height: 40px;
+    width: 40px;
+  }
 
-      .sync-row {
-        align-items: center;
-        flex: auto;
-      }
+  #sync-setup {
+    --cr-secondary-text-color: var(--cros-text-color-alert);
+  }
 
-      #profile-icon {
-        background: center / cover no-repeat;
-        border-radius: 20px;
-        flex-shrink: 0;
-        height: 40px;
-        width: 40px;
-      }
+  cr-link-row {
+    --cr-link-row-icon-width: var(--icon-width);
+    border-top: var(--cr-separator-line);
+  }
 
-      #sync-setup {
-        --cr-secondary-text-color: var(--cros-text-color-alert);
-      }
+  settings-parental-controls-page {
+    --cr-link-row-icon-width: var(--icon-width);
+  }
 
-      cr-link-row {
-        --cr-link-row-icon-width: var(--icon-width);
-        border-top: var(--cr-separator-line);
-      }
-
-      settings-parental-controls-page {
-        --cr-link-row-icon-width: var(--icon-width);
-      }
-
-      .icon-container {
-        display: flex;
-        flex-shrink: 0;
-        justify-content: center;
-        width: 40px;
-      }
-
-    </style>
-    <settings-animated-pages id="pages" section="osPeople"
-        focus-config="[[focusConfig_]]">
-      <div route-path="default">
-        <div class="settings-box first two-line">
-          <template is="dom-if" if="[[syncStatus]]">
-            <!-- Does not use <cr-link-row> due to custom aria label. -->
-            <div id="profile-icon"
-                style="background-image: [[getIconImageSet_(profileIconUrl_)]]"
-                on-click="onAccountManagerTap_"
-                actionable$="[[isAccountManagerEnabled_]]">
-            </div>
-            <div class="middle two-line no-min-width"
-                id="profile-row"
-                on-click="onAccountManagerTap_"
-                actionable$="[[isAccountManagerEnabled_]]">
-              <div class="flex text-elide settings-box-text">
-                <span id="profile-name" aria-hidden="true">
-                  [[getProfileName_(profileName_)]]
-                </span>
-                <div id="profile-label" class="secondary"
-                    aria-hidden="true">
-                  [[profileLabel_]]
-                </div>
-              </div>
-              <cr-icon-button class="subpage-arrow"
-                  hidden="[[!isAccountManagerEnabled_]]"
-                  id="account-manager-subpage-trigger"
-                  aria-label="$i18n{accountManagerSubMenuLabel}"
-                  aria-describedby="profile-name profile-label"
-                  aria-roledescription="$i18n{subpageArrowRoleDescription}">
-              </cr-icon-button>
-            </div>
-          </template>
+  .icon-container {
+    display: flex;
+    flex-shrink: 0;
+    justify-content: center;
+    width: 40px;
+  }
+</style>
+<settings-animated-pages id="pages" section="osPeople"
+    focus-config="[[focusConfig_]]">
+  <div route-path="default">
+    <div class="settings-box first two-line">
+      <template is="dom-if" if="[[syncStatus]]">
+        <!-- Does not use <cr-link-row> due to custom aria label. -->
+        <div id="profile-icon"
+            style="background-image: [[getIconImageSet_(profileIconUrl_)]]"
+            on-click="onAccountManagerTap_"
+            actionable$="[[isAccountManagerEnabled_]]">
         </div>
-
-        <cr-link-row id="sync-setup"
-            start-icon="cr:sync"
-            label="$i18n{syncAndNonPersonalizedServices}"
-            sub-label="[[getSyncAndGoogleServicesSubtext_(syncStatus)]]"
-            on-click="onSyncTap_"
-            role-description="$i18n{subpageArrowRoleDescription}">
-        </cr-link-row>
-
-        <template is="dom-if" if="[[showParentalControls_]]">
-          <settings-parental-controls-page>
-          </settings-parental-controls-page>
-        </template>
-
-      </div>
-
-      <template is="dom-if" route-path="/syncSetup">
-        <settings-subpage
-            page-title="$i18n{syncPageTitle}"
-            learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
-          <settings-sync-page
-              sync-status="[[syncStatus]]" prefs="{{prefs}}"
-              page-visibility="[[pageVisibility.privacy]]"
-              focus-config="[[focusConfig_]]">
-          </settings-sync-page>
-        </settings-subpage>
+        <div class="middle two-line no-min-width"
+            id="profile-row"
+            on-click="onAccountManagerTap_"
+            actionable$="[[isAccountManagerEnabled_]]">
+          <div class="flex text-elide settings-box-text">
+            <span id="profile-name" aria-hidden="true">
+              [[getProfileName_(profileName_)]]
+            </span>
+            <div id="profile-label" class="secondary"
+                aria-hidden="true">
+              [[profileLabel_]]
+            </div>
+          </div>
+          <cr-icon-button class="subpage-arrow"
+              hidden="[[!isAccountManagerEnabled_]]"
+              id="account-manager-subpage-trigger"
+              aria-label="$i18n{accountManagerSubMenuLabel}"
+              aria-describedby="profile-name profile-label"
+              aria-roledescription="$i18n{subpageArrowRoleDescription}">
+          </cr-icon-button>
+        </div>
       </template>
-
-      <!-- Pre-SyncSettingsCategorization we show the browser's sync controls.
-      -->
-      <template is="dom-if" if="[[!syncSettingsCategorizationEnabled_]]">
-        <template is="dom-if" route-path="/syncSetup/advanced">
-          <settings-subpage page-title="$i18n{syncAdvancedPageTitle}"
-              learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
-            <settings-sync-controls sync-status="[[syncStatus]]">
-            </settings-sync-controls>
-          </settings-subpage>
-        </template>
-      </template>
-
-      <!-- Post-SyncSettingsCategorization we have separate OS sync controls.
-      -->
-      <template is="dom-if" if="[[syncSettingsCategorizationEnabled_]]">
-        <template is="dom-if" route-path="/osSync">
-          <settings-subpage page-title="$i18n{syncAndNonPersonalizedServices}"
-              learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
-            <os-sync-controls sync-status="[[syncStatus]]"
-                profile-icon-url="[[profileIconUrl_]]"
-                profile-name="[[profileName_]]"
-                profile-email="[[profileEmail_]]">
-            </os-sync-controls>
-          </settings-subpage>
-        </template>
-      </template>
-
-      <template is="dom-if" route-path="/accountManager">
-        <settings-subpage page-title="$i18n{accountManagerPageTitle}">
-          <settings-account-manager prefs="[[prefs]]">
-          </settings-account-manager>
-        </settings-subpage>
-      </template>
-    </settings-animated-pages>
-
-    <template is="dom-if" if="[[showSignoutDialog_]]" restamp>
-      <settings-signout-dialog sync-status="[[syncStatus]]"
-          on-close="onDisconnectDialogClosed_">
-      </settings-signout-dialog>
+    </div>
+    <cr-link-row id="sync-setup"
+        start-icon="cr:sync"
+        label="$i18n{syncAndNonPersonalizedServices}"
+        sub-label="[[getSyncAndGoogleServicesSubtext_(syncStatus)]]"
+        on-click="onSyncTap_"
+        role-description="$i18n{subpageArrowRoleDescription}">
+    </cr-link-row>
+    <template is="dom-if" if="[[showParentalControls_]]">
+      <settings-parental-controls-page>
+      </settings-parental-controls-page>
     </template>
-
+  </div>
+  <template is="dom-if" route-path="/syncSetup">
+    <settings-subpage
+        page-title="$i18n{syncPageTitle}"
+        learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
+      <settings-sync-page
+          sync-status="[[syncStatus]]" prefs="{{prefs}}"
+          page-visibility="[[pageVisibility.privacy]]"
+          focus-config="[[focusConfig_]]">
+      </settings-sync-page>
+    </settings-subpage>
   </template>
-  <script src="os_people_page.js"></script>
-</dom-module>
+  <!-- Pre-SyncSettingsCategorization we show the browser's sync controls.
+  -->
+  <template is="dom-if" if="[[!syncSettingsCategorizationEnabled_]]">
+    <template is="dom-if" route-path="/syncSetup/advanced">
+      <settings-subpage page-title="$i18n{syncAdvancedPageTitle}"
+          learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
+        <settings-sync-controls sync-status="[[syncStatus]]">
+        </settings-sync-controls>
+      </settings-subpage>
+    </template>
+  </template>
+  <!-- Post-SyncSettingsCategorization we have separate OS sync controls.
+  -->
+  <template is="dom-if" if="[[syncSettingsCategorizationEnabled_]]">
+    <template is="dom-if" route-path="/osSync">
+      <settings-subpage page-title="$i18n{syncAndNonPersonalizedServices}"
+          learn-more-url="$i18n{syncAndGoogleServicesLearnMoreURL}">
+        <os-sync-controls sync-status="[[syncStatus]]"
+            profile-icon-url="[[profileIconUrl_]]"
+            profile-name="[[profileName_]]"
+            profile-email="[[profileEmail_]]">
+        </os-sync-controls>
+      </settings-subpage>
+    </template>
+  </template>
+  <template is="dom-if" route-path="/accountManager">
+    <settings-subpage page-title="$i18n{accountManagerPageTitle}">
+      <settings-account-manager prefs="[[prefs]]">
+      </settings-account-manager>
+    </settings-subpage>
+  </template>
+</settings-animated-pages>
+<template is="dom-if" if="[[showSignoutDialog_]]" restamp>
+  <settings-signout-dialog sync-status="[[syncStatus]]"
+      on-close="onDisconnectDialogClosed_">
+  </settings-signout-dialog>
+</template>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
index fd25c90..0391c17 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.js
@@ -6,12 +6,55 @@
  * @fileoverview
  * 'settings-people-page' is the settings page containing sign-in settings.
  */
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import '//resources/cr_elements/cr_link_row/cr_link_row.js';
+import '//resources/cr_elements/icons.m.js';
+import '//resources/cr_elements/policy/cr_policy_indicator.m.js';
+import '//resources/cr_elements/shared_vars_css.m.js';
+import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import '../../controls/settings_toggle_button.js';
+import '../../people_page/signout_dialog.js';
+import '../../people_page/sync_controls.js';
+import '../../people_page/sync_page.js';
+import '../../settings_page/settings_animated_pages.js';
+import '../../settings_page/settings_subpage.js';
+import '../../settings_shared_css.js';
+import '../parental_controls_page/parental_controls_page.js';
+import './account_manager.js';
+import './fingerprint_list.js';
+import './lock_screen.js';
+import './lock_screen_password_prompt_dialog.js';
+import './users_page.js';
+import './os_sync_controls.js';
+
+import {convertImageSequenceToPng, isEncodedPngDataUrlAnimated} from '//resources/cr_elements/chromeos/cr_picture/png.js';
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {addWebUIListener, removeWebUIListener, sendWithPromise, WebUIListener} from '//resources/js/cr.m.js';
+import {focusWithoutInk} from '//resources/js/cr/ui/focus_without_ink.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {getImage} from '//resources/js/icon.js';
+import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {loadTimeData} from '../../i18n_setup.js';
+import {ProfileInfoBrowserProxyImpl} from '../../people_page/profile_info_browser_proxy.js';
+import {StatusAction, SyncBrowserProxyImpl} from '../../people_page/sync_browser_proxy.js';
+import {Route, Router} from '../../router.js';
+import {DeepLinkingBehavior} from '../deep_linking_behavior.m.js';
+import {OSPageVisibility, osPageVisibility} from '../os_page_visibility.m.js';
+import {routes} from '../os_route.m.js';
+import {RouteObserverBehavior} from '../route_observer_behavior.js';
+
+import {Account, AccountManagerBrowserProxyImpl} from './account_manager_browser_proxy.js';
+import {LockStateBehavior} from './lock_state_behavior.m.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'os-settings-people-page',
 
   behaviors: [
     DeepLinkingBehavior,
-    settings.RouteObserverBehavior,
+    RouteObserverBehavior,
     I18nBehavior,
     WebUIListenerBehavior,
     LockStateBehavior,
@@ -36,7 +79,7 @@
 
     /**
      * The current sync status, supplied by SyncBrowserProxy.
-     * @type {?settings.SyncStatus}
+     * @type {?SyncStatus}
      */
     syncStatus: Object,
 
@@ -121,22 +164,18 @@
       type: Object,
       value() {
         const map = new Map();
-        if (settings.routes.SYNC) {
-          map.set(settings.routes.SYNC.path, '#sync-setup');
+        if (routes.SYNC) {
+          map.set(routes.SYNC.path, '#sync-setup');
         }
-        if (settings.routes.LOCK_SCREEN) {
-          map.set(
-              settings.routes.LOCK_SCREEN.path, '#lock-screen-subpage-trigger');
+        if (routes.LOCK_SCREEN) {
+          map.set(routes.LOCK_SCREEN.path, '#lock-screen-subpage-trigger');
         }
-        if (settings.routes.ACCOUNTS) {
-          map.set(
-              settings.routes.ACCOUNTS.path,
-              '#manage-other-people-subpage-trigger');
+        if (routes.ACCOUNTS) {
+          map.set(routes.ACCOUNTS.path, '#manage-other-people-subpage-trigger');
         }
-        if (settings.routes.ACCOUNT_MANAGER) {
+        if (routes.ACCOUNT_MANAGER) {
           map.set(
-              settings.routes.ACCOUNT_MANAGER.path,
-              '#account-manager-subpage-trigger');
+              routes.ACCOUNT_MANAGER.path, '#account-manager-subpage-trigger');
         }
         return map;
       },
@@ -176,7 +215,7 @@
     },
   },
 
-  /** @private {?settings.SyncBrowserProxy} */
+  /** @private {?SyncBrowserProxy} */
   syncBrowserProxy_: null,
 
   /** @override */
@@ -188,13 +227,13 @@
       this.updateAccounts_();
     } else {
       // Otherwise use the Profile name and icon.
-      settings.ProfileInfoBrowserProxyImpl.getInstance().getProfileInfo().then(
+      ProfileInfoBrowserProxyImpl.getInstance().getProfileInfo().then(
           this.handleProfileInfo_.bind(this));
       this.addWebUIListener(
           'profile-info-changed', this.handleProfileInfo_.bind(this));
     }
 
-    this.syncBrowserProxy_ = settings.SyncBrowserProxyImpl.getInstance();
+    this.syncBrowserProxy_ = SyncBrowserProxyImpl.getInstance();
     this.syncBrowserProxy_.getSyncStatus().then(
         this.handleSyncStatus_.bind(this));
     this.addWebUIListener(
@@ -216,7 +255,7 @@
   onPasswordPromptDialogClose_() {
     this.showPasswordPromptDialog_ = false;
     if (!this.setModes_) {
-      settings.Router.getInstance().navigateToPreviousRoute();
+      Router.getInstance().navigateToPreviousRoute();
     }
   },
 
@@ -228,7 +267,7 @@
    */
   afterRenderShowDeepLink_(settingId, getElementCallback) {
     // Wait for element to load.
-    Polymer.RenderStatus.afterNextRender(this, () => {
+    afterNextRender(this, () => {
       const deepLinkElement = getElementCallback();
       if (!deepLinkElement || deepLinkElement.hidden) {
         console.warn(`Element with deep link id ${settingId} not focusable.`);
@@ -264,7 +303,7 @@
               this.$$('settings-sync-page'));
           // Expand the encryption collapse.
           syncPage.forceEncryptionExpanded = true;
-          Polymer.dom.flush();
+          flush();
           return syncPage && syncPage.getEncryptionOptions() &&
               syncPage.getEncryptionOptions().getEncryptionsRadioButtons();
         });
@@ -304,19 +343,18 @@
   },
 
   /**
-   * settings.RouteObserverBehavior
-   * @param {!settings.Route} route
-   * @param {!settings.Route} oldRoute
+   * RouteObserverBehavior
+   * @param {!Route} route
+   * @param {!Route} oldRoute
    * @protected
    */
   currentRouteChanged(route, oldRoute) {
-    if (settings.Router.getInstance().getCurrentRoute() ===
-        settings.routes.OS_SIGN_OUT) {
+    if (Router.getInstance().getCurrentRoute() === routes.OS_SIGN_OUT) {
       // If the sync status has not been fetched yet, optimistically display
       // the sign-out dialog. There is another check when the sync status is
       // fetched. The dialog will be closed when the user is not signed in.
       if (this.syncStatus && !this.syncStatus.signedIn) {
-        settings.Router.getInstance().navigateToPreviousRoute();
+        Router.getInstance().navigateToPreviousRoute();
       } else {
         this.showSignoutDialog_ = true;
       }
@@ -324,7 +362,7 @@
 
     // The old sync page is a shared subpage, so we handle deep links for
     // both this page and the sync page. Not ideal.
-    if (route === settings.routes.SYNC || route === settings.routes.OS_PEOPLE) {
+    if (route === routes.SYNC || route === routes.OS_PEOPLE) {
       this.attemptDeepLink();
     }
   },
@@ -352,14 +390,14 @@
   /**
    * Handler for when the profile's icon and name is updated.
    * @private
-   * @param {!settings.ProfileInfo} info
+   * @param {!ProfileInfo} info
    */
   handleProfileInfo_(info) {
     this.profileName_ = info.name;
     // Extract first frame from image by creating a single frame PNG using
     // url as input if base64 encoded and potentially animated.
     if (info.iconUrl.startsWith('data:image/png;base64')) {
-      this.profileIconUrl_ = cr.png.convertImageSequenceToPng([info.iconUrl]);
+      this.profileIconUrl_ = convertImageSequenceToPng([info.iconUrl]);
       return;
     }
     this.profileIconUrl_ = info.iconUrl;
@@ -370,9 +408,8 @@
    * @private
    */
   updateAccounts_: async function() {
-    const /** @type {!Array<settings.Account>} */ accounts =
-        await settings.AccountManagerBrowserProxyImpl.getInstance()
-            .getAccounts();
+    const /** @type {!Array<Account>} */ accounts =
+        await AccountManagerBrowserProxyImpl.getInstance().getAccounts();
     // The user might not have any GAIA accounts (e.g. guest mode or Active
     // Directory). In these cases the profile row is hidden, so there's nothing
     // to do.
@@ -387,12 +424,12 @@
   },
 
   /**
-   * @param {!Array<settings.Account>} accounts
+   * @param {!Array<Account>} accounts
    * @private
    */
   async setProfileLabel(accounts) {
     // Template: "$1 Google accounts" with correct plural of "account".
-    const labelTemplate = await cr.sendWithPromise(
+    const labelTemplate = await sendWithPromise(
         'getPluralString', 'profileLabel', accounts.length);
 
     // Final output: "X Google accounts"
@@ -402,7 +439,7 @@
 
   /**
    * Handler for when the sync state is pushed from the browser.
-   * @param {?settings.SyncStatus} syncStatus
+   * @param {?SyncStatus} syncStatus
    * @private
    */
   handleSyncStatus_(syncStatus) {
@@ -419,23 +456,22 @@
   /** @private */
   onDisconnectDialogClosed_(e) {
     this.showSignoutDialog_ = false;
-    cr.ui.focusWithoutInk(assert(this.$$('#disconnectButton')));
+    focusWithoutInk(assert(this.$$('#disconnectButton')));
 
-    if (settings.Router.getInstance().getCurrentRoute() ===
-        settings.routes.OS_SIGN_OUT) {
-      settings.Router.getInstance().navigateToPreviousRoute();
+    if (Router.getInstance().getCurrentRoute() === routes.OS_SIGN_OUT) {
+      Router.getInstance().navigateToPreviousRoute();
     }
   },
 
   /** @private */
   onDisconnectTap_() {
-    settings.Router.getInstance().navigateTo(settings.routes.OS_SIGN_OUT);
+    Router.getInstance().navigateTo(routes.OS_SIGN_OUT);
   },
 
   /** @private */
   onSyncTap_() {
     // Users can go to sync subpage regardless of sync status.
-    settings.Router.getInstance().navigateTo(settings.routes.SYNC);
+    Router.getInstance().navigateTo(routes.SYNC);
   },
 
   /**
@@ -444,7 +480,7 @@
    */
   onAccountManagerTap_(e) {
     if (this.isAccountManagerEnabled_) {
-      settings.Router.getInstance().navigateTo(settings.routes.ACCOUNT_MANAGER);
+      Router.getInstance().navigateTo(routes.ACCOUNT_MANAGER);
     }
   },
 
@@ -454,7 +490,7 @@
    * @private
    */
   getIconImageSet_(iconUrl) {
-    return cr.icon.getImage(iconUrl);
+    return getImage(iconUrl);
   },
 
   /**
@@ -469,7 +505,7 @@
   },
 
   /**
-   * @param {!settings.SyncStatus} syncStatus
+   * @param {!SyncStatus} syncStatus
    * @return {boolean} Whether to show the "Sign in to Chrome" button.
    * @private
    */
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html
index 92534d9..a7d86dd 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html
@@ -1,222 +1,189 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared">
+  :host {
+    --shown-avatar-size: 40px;
+    --sync-icon-border-size: 2px;
+    --sync-icon-size: 16px;
+  }
 
-<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/load_time_data.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="chrome://resources/cr_components/chromeos/localized_link/localized_link.html">
-<link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="../metrics_recorder.html">
-<link rel="import" href="../os_route.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../route_observer_behavior.html">
-<link rel="import" href="os_sync_browser_proxy.html">
-<link rel="import" href="../../people_page/sync_browser_proxy.html">
+  #avatarContainer {
+    height: var(--shown-avatar-size);
+    position: relative;
+  }
 
-<dom-module id="os-sync-controls">
-  <template>
-    <style include="settings-shared">
-      :host {
-        --shown-avatar-size: 40px;
-        --sync-icon-border-size: 2px;
-        --sync-icon-size: 16px;
-      }
+  #avatarIcon {
+    border-radius: 20px;
+    flex-shrink: 0;
+    height: var(--shown-avatar-size);
+    width: var(--shown-avatar-size);
+  }
 
-      #avatarContainer {
-        height: var(--shown-avatar-size);
-        position: relative;
-      }
+  /* Similar to browser settings-sync-account-control styling. */
+  #syncIconContainer {     align-items: center;
+    background: var(--cros-icon-color-positive);
+    border: var(--sync-icon-border-size) solid
+        var(--cros-bg-color-elevation-1);
+    border-radius: 50%;
+    display: flex;
+    height: var(--sync-icon-size);
+    position: absolute;
+    right: -6px;
+    top: calc(var(--shown-avatar-size) - var(--sync-icon-size) -
+        var(--sync-icon-border-size));
+    width: var(--sync-icon-size);
+  }
 
-      #avatarIcon {
-        border-radius: 20px;
-        flex-shrink: 0;
-        height: var(--shown-avatar-size);
-        width: var(--shown-avatar-size);
-      }
+  :host-context([dir='rtl']) #syncIconContainer {
+    left: -6px;
+    right: initial;
+  }
 
-      /* Similar to browser settings-sync-account-control styling. */
-      #syncIconContainer {
-        align-items: center;
-        background: var(--cros-icon-color-positive);
-        border: var(--sync-icon-border-size) solid
-            var(--cros-bg-color-elevation-1);
-        border-radius: 50%;
-        display: flex;
-        height: var(--sync-icon-size);
-        position: absolute;
-        right: -6px;
-        top: calc(var(--shown-avatar-size) - var(--sync-icon-size) -
-            var(--sync-icon-border-size));
-        width: var(--sync-icon-size);
-      }
+  #syncIconContainer.sync-problem {
+    background: var(--cros-icon-color-alert);
+  }
 
-      :host-context([dir='rtl']) #syncIconContainer {
-        left: -6px;
-        right: initial;
-      }
+  #syncIconContainer.sync-paused {
+    background: var(--cros-icon-color-prominent);
+  }
 
-      #syncIconContainer.sync-problem {
-        background: var(--cros-icon-color-alert);
-      }
+  #syncIconContainer.sync-disabled {
+    background: var(--cros-icon-color-disabled);
+  }
 
-      #syncIconContainer.sync-paused {
-        background: var(--cros-icon-color-prominent);
-      }
+  #syncIconContainer iron-icon {
+    fill: var(--cros-bg-color-elevation-1);
+    height: 12px;
+    margin: auto;
+    width: 12px;
+  }
 
-      #syncIconContainer.sync-disabled {
-        background: var(--cros-icon-color-disabled);
-      }
+  .settings-box {
+    border-top: none;
+  }
 
-      #syncIconContainer iron-icon {
-        fill: var(--cros-bg-color-elevation-1);
-        height: 12px;
-        margin: auto;
-        width: 12px;
-      }
+  .flex {
+    display: flex;
+    flex: 1;
+    flex-direction: column;
+  }
 
-      .settings-box {
-        border-top: none;
-      }
+  .list-item {
+    display: flex;
+  }
 
-      .flex {
-        display: flex;
-        flex: 1;
-        flex-direction: column;
-      }
+  .list-item > div {
+    flex: 1;
+  }
 
-      .list-item {
-        display: flex;
-      }
-
-      .list-item > div {
-        flex: 1;
-      }
-
-      div[label-disabled] {
-        opacity: var(--cr-disabled-opacity);
-      }
-    </style>
-    <div class="settings-box first two-line">
-      <div id="avatarContainer">
-        <img id="avatarIcon" alt="" src="[[profileIconUrl]]">
-        <div id="syncIconContainer" hidden="[[!osSyncFeatureEnabled]]"
-            class$="[[getSyncIconStyle_(
-                syncStatus.hasError, syncStatus.statusAction,
-                syncStatus.disabled)]]">
-          <iron-icon icon$="[[getSyncIcon_(
-              syncStatus.hasError, syncStatus.statusAction,
-              syncStatus.disabled)]]"></iron-icon>
-        </div>
-      </div>
-      <div class="middle two-line no-min-width">
-        <div class="flex text-elide settings-box-text">
-          <span id="accountTitle" aria-hidden="true">
-            [[getAccountTitle_(profileName, syncStatus.hasError)]]
-          </span>
-          <div id="accountSubtitle" class="secondary" aria-hidden="true">
-            [[getAccountSubtitle_(profileEmail, syncStatus.hasError,
-                osSyncFeatureEnabled)]]
-          </div>
-        </div>
-      </div>
-
-      <template is="dom-if" if="[[syncConsentOptionalEnabled_]]" restamp>
-        <cr-button id="syncOnOffButton"
-            class="action-button"
-            on-click="onSyncOnOffButtonClick_"
-            aria-labelledby="syncOnOffButton accountTitle accountSubtitle"
-            aria-describedby="featureLabel"
-            deep-link-focus-id$="[[Setting.kSplitSyncOnOff]]">
-          [[getSyncOnOffButtonLabel_(osSyncFeatureEnabled)]]
-        </cr-button>
-      </template>
+  div[label-disabled] {
+    opacity: var(--cr-disabled-opacity);
+  }
+</style>
+<div class="settings-box first two-line">
+  <div id="avatarContainer">
+    <img id="avatarIcon" alt="" src="[[profileIconUrl]]">
+    <div id="syncIconContainer" hidden="[[!osSyncFeatureEnabled]]"
+        class$="[[getSyncIconStyle_(
+            syncStatus.hasError, syncStatus.statusAction,
+            syncStatus.disabled)]]">
+      <iron-icon icon$="[[getSyncIcon_(
+          syncStatus.hasError, syncStatus.statusAction,
+          syncStatus.disabled)]]"></iron-icon>
     </div>
-
-    <div id="featureLabel" class="settings-box">
-      <localized-link class="secondary"
-          localized-string="$i18n{osSyncFeatureLabel}"
-          link-url="$i18n{browserSettingsSyncSetupUrl}">
-      </localized-link>
+  </div>
+  <div class="middle two-line no-min-width">
+    <div class="flex text-elide settings-box-text">
+      <span id="accountTitle" aria-hidden="true">
+        [[getAccountTitle_(profileName, syncStatus.hasError)]]
+      </span>
+      <div id="accountSubtitle" class="secondary" aria-hidden="true">
+        [[getAccountSubtitle_(profileEmail, syncStatus.hasError,
+            osSyncFeatureEnabled)]]
+      </div>
     </div>
-
-    <div class="settings-box">
-      <div id="syncEverythingCheckboxLabel"
-          class="start"
-          label-disabled$="[[!osSyncFeatureEnabled]]">
-        $i18n{syncEverythingCheckboxLabel}
-      </div>
-      <cr-toggle id="syncAllOsTypesControl"
-          checked="{{osSyncPrefs.syncAllOsTypes}}"
-          on-change="onSyncAllOsTypesChanged_"
-          disabled="[[!osSyncFeatureEnabled]]"
-          aria-labelledby="syncEverythingCheckboxLabel">
-      </cr-toggle>
-    </div>
-
-    <div class="list-frame" id="sync-data-types">
-      <div class="list-item" hidden="[[!osSyncPrefs.osAppsRegistered]]">
-        <div id="osAppsCheckboxLabel"
-            label-disabled$="[[areDataTypeTogglesDisabled_]]">
-          $i18n{osSyncAppsCheckboxLabel}
-        </div>
-        <cr-toggle id="osAppsControl"
-            checked="{{osSyncPrefs.osAppsSynced}}"
-            on-change="onAppsSyncedChanged_"
-            disabled="[[areDataTypeTogglesDisabled_]]"
-            aria-labelledby="osAppsCheckboxLabel">
-        </cr-toggle>
-      </div>
-
-      <div class="list-item" hidden="[[!osSyncPrefs.osPreferencesRegistered]]">
-        <div id="osSettingsCheckboxLabel"
-            label-disabled$="[[areDataTypeTogglesDisabled_]]">
-          $i18n{osSyncSettingsCheckboxLabel}
-        </div>
-        <cr-toggle id="osPreferencesControl"
-            checked="{{osSyncPrefs.osPreferencesSynced}}"
-            on-change="onSingleSyncDataTypeChanged_"
-            disabled="[[areDataTypeTogglesDisabled_]]"
-            aria-labelledby="osSettingsCheckboxLabel">
-        </cr-toggle>
-      </div>
-
-      <div class="list-item"
-           hidden="[[!osSyncPrefs.osWifiConfigurationsRegistered]]">
-        <div id="osWifiConfigurationsCheckboxLabel"
-            label-disabled$="[[areDataTypeTogglesDisabled_]]">
-          $i18n{osSyncWifiConfigurationsCheckboxLabel}
-        </div>
-        <cr-toggle checked="{{osSyncPrefs.osWifiConfigurationsSynced}}"
-            on-change="onSingleSyncDataTypeChanged_"
-            disabled="[[areDataTypeTogglesDisabled_]]"
-            aria-labelledby="osWifiConfigurationsCheckboxLabel">
-        </cr-toggle>
-      </div>
-
-      <div class="list-item" hidden="[[!osSyncPrefs.osAppsRegistered]]">
-        <!-- Wallpaper sync is a special case; its implementation relies upon
-             the chrome.storage.sync API, which is controlled by the apps
-             toggle. Thus, the wallpaper label and toggle are only shown when
-             apps are registered and are disabled if apps syncing is off.
-             TODO(https://crbug.com/967987): Break this dependency. -->
-        <div id="wallpaperCheckboxLabel"
-            label-disabled$="[[shouldWallpaperSyncSectionBeDisabled_(
-                areDataTypeTogglesDisabled_, osSyncPrefs.osAppsSynced)]]">
-          $i18n{wallpaperCheckboxLabel}
-        </div>
-        <cr-toggle checked="{{osSyncPrefs.wallpaperEnabled}}"
-            on-change="onSingleSyncDataTypeChanged_"
-            disabled="[[shouldWallpaperSyncSectionBeDisabled_(
-                areDataTypeTogglesDisabled_, osSyncPrefs.osAppsSynced)]]"
-            aria-labelledby="wallpaperCheckboxLabel">
-        </cr-toggle>
-      </div>
-
-    </div>
+  </div>
+  <template is="dom-if" if="[[syncConsentOptionalEnabled_]]" restamp>
+    <cr-button id="syncOnOffButton"
+        class="action-button"
+        on-click="onSyncOnOffButtonClick_"
+        aria-labelledby="syncOnOffButton accountTitle accountSubtitle"
+        aria-describedby="featureLabel"
+        deep-link-focus-id$="[[Setting.kSplitSyncOnOff]]">
+      [[getSyncOnOffButtonLabel_(osSyncFeatureEnabled)]]
+    </cr-button>
   </template>
-  <script src="os_sync_controls.js"></script>
-</dom-module>
+</div>
+<div id="featureLabel" class="settings-box">
+  <localized-link class="secondary"
+      localized-string="$i18n{osSyncFeatureLabel}"
+      link-url="$i18n{browserSettingsSyncSetupUrl}">
+  </localized-link>
+</div>
+<div class="settings-box">
+  <div id="syncEverythingCheckboxLabel"
+      class="start"
+      label-disabled$="[[!osSyncFeatureEnabled]]">
+    $i18n{syncEverythingCheckboxLabel}
+  </div>
+  <cr-toggle id="syncAllOsTypesControl"
+      checked="{{osSyncPrefs.syncAllOsTypes}}"
+      on-change="onSyncAllOsTypesChanged_"
+      disabled="[[!osSyncFeatureEnabled]]"
+      aria-labelledby="syncEverythingCheckboxLabel">
+  </cr-toggle>
+</div>
+<div class="list-frame" id="sync-data-types">
+  <div class="list-item" hidden="[[!osSyncPrefs.osAppsRegistered]]">
+    <div id="osAppsCheckboxLabel"
+        label-disabled$="[[areDataTypeTogglesDisabled_]]">
+      $i18n{osSyncAppsCheckboxLabel}
+    </div>
+    <cr-toggle id="osAppsControl"
+        checked="{{osSyncPrefs.osAppsSynced}}"
+        on-change="onAppsSyncedChanged_"
+        disabled="[[areDataTypeTogglesDisabled_]]"
+        aria-labelledby="osAppsCheckboxLabel">
+    </cr-toggle>
+  </div>
+  <div class="list-item" hidden="[[!osSyncPrefs.osPreferencesRegistered]]">
+    <div id="osSettingsCheckboxLabel"
+        label-disabled$="[[areDataTypeTogglesDisabled_]]">
+      $i18n{osSyncSettingsCheckboxLabel}
+    </div>
+    <cr-toggle id="osPreferencesControl"
+        checked="{{osSyncPrefs.osPreferencesSynced}}"
+        on-change="onSingleSyncDataTypeChanged_"
+        disabled="[[areDataTypeTogglesDisabled_]]"
+        aria-labelledby="osSettingsCheckboxLabel">
+    </cr-toggle>
+  </div>
+  <div class="list-item"
+       hidden="[[!osSyncPrefs.osWifiConfigurationsRegistered]]">
+    <div id="osWifiConfigurationsCheckboxLabel"
+        label-disabled$="[[areDataTypeTogglesDisabled_]]">
+      $i18n{osSyncWifiConfigurationsCheckboxLabel}
+    </div>
+    <cr-toggle checked="{{osSyncPrefs.osWifiConfigurationsSynced}}"
+        on-change="onSingleSyncDataTypeChanged_"
+        disabled="[[areDataTypeTogglesDisabled_]]"
+        aria-labelledby="osWifiConfigurationsCheckboxLabel">
+    </cr-toggle>
+  </div>
+  <div class="list-item" hidden="[[!osSyncPrefs.osAppsRegistered]]">
+    <!-- Wallpaper sync is a special case; its implementation relies upon
+         the chrome.storage.sync API, which is controlled by the apps
+         toggle. Thus, the wallpaper label and toggle are only shown when
+         apps are registered and are disabled if apps syncing is off.
+         TODO(https://crbug.com/967987): Break this dependency. -->
+    <div id="wallpaperCheckboxLabel"
+        label-disabled$="[[shouldWallpaperSyncSectionBeDisabled_(
+            areDataTypeTogglesDisabled_, osSyncPrefs.osAppsSynced)]]">
+      $i18n{wallpaperCheckboxLabel}
+    </div>
+    <cr-toggle checked="{{osSyncPrefs.wallpaperEnabled}}"
+        on-change="onSingleSyncDataTypeChanged_"
+        disabled="[[shouldWallpaperSyncSectionBeDisabled_(
+            areDataTypeTogglesDisabled_, osSyncPrefs.osAppsSynced)]]"
+        aria-labelledby="wallpaperCheckboxLabel">
+    </cr-toggle>
+  </div>
+</div>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
index 6d53b7b..3b2a5262 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.js
@@ -2,11 +2,31 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-(function() {
+import '//resources/cr_elements/cr_toggle/cr_toggle.m.js';
+import '//resources/cr_elements/shared_vars_css.m.js';
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '../../settings_shared_css.js';
+import '//resources/cr_components/chromeos/localized_link/localized_link.js';
+
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {loadTimeData} from '//resources/js/load_time_data.m.js';
+import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {StatusAction, SyncBrowserProxyImpl} from '../../people_page/sync_browser_proxy.js';
+import {Route, Router} from '../../router.js';
+import {DeepLinkingBehavior} from '../deep_linking_behavior.m.js';
+import {recordClick, recordNavigation, recordPageBlur, recordPageFocus, recordSearch, recordSettingChange, setUserActionRecorderForTesting} from '../metrics_recorder.m.js';
+import {routes} from '../os_route.m.js';
+import {RouteObserverBehavior} from '../route_observer_behavior.js';
+
+import {OsSyncBrowserProxy, OsSyncBrowserProxyImpl, OsSyncPrefs} from './os_sync_browser_proxy.m.js';
+
 
 /**
  * Names of the individual data type properties to be cached from
- * settings.OsSyncPrefs when the user checks 'Sync All'.
+ * OsSyncPrefs when the user checks 'Sync All'.
  * @type {!Array<string>}
  */
 const SyncPrefsIndividualDataTypes = [
@@ -24,12 +44,13 @@
  * 'os-sync-controls' contains all OS sync data type controls.
  */
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'os-sync-controls',
 
   behaviors: [
     DeepLinkingBehavior,
     I18nBehavior,
-    settings.RouteObserverBehavior,
+    RouteObserverBehavior,
     WebUIListenerBehavior,
   ],
 
@@ -44,7 +65,7 @@
     /**
      * Injected sync system status. Undefined until the parent component injects
      * the value.
-     * @type {settings.SyncStatus|undefined}
+     * @type {SyncStatus|undefined}
      */
     syncStatus: Object,
 
@@ -77,7 +98,7 @@
      * The current OS sync preferences. Cached so we can restore individual
      * toggle state when turning "sync everything" on and off, without affecting
      * the underlying chrome prefs.
-     * @type {settings.OsSyncPrefs|undefined}
+     * @type {OsSyncPrefs|undefined}
      */
     osSyncPrefs: Object,
 
@@ -107,7 +128,7 @@
     },
   },
 
-  /** @private {?settings.OsSyncBrowserProxy} */
+  /** @private {?OsSyncBrowserProxy} */
   browserProxy_: null,
 
   /**
@@ -119,7 +140,7 @@
 
   /** @override */
   created() {
-    this.browserProxy_ = settings.OsSyncBrowserProxyImpl.getInstance();
+    this.browserProxy_ = OsSyncBrowserProxyImpl.getInstance();
   },
 
   /** @override */
@@ -129,17 +150,17 @@
   },
 
   /**
-   * settings.RouteObserverBehavior
-   * @param {!settings.Route|undefined} newRoute
-   * @param {!settings.Route|undefined} oldRoute
+   * RouteObserverBehavior
+   * @param {!Route|undefined} newRoute
+   * @param {!Route|undefined} oldRoute
    * @protected
    */
   currentRouteChanged(newRoute, oldRoute) {
-    if (newRoute === settings.routes.OS_SYNC) {
+    if (newRoute === routes.OS_SYNC) {
       this.browserProxy_.didNavigateToOsSyncPage();
       this.attemptDeepLink();
     }
-    if (oldRoute === settings.routes.OS_SYNC) {
+    if (oldRoute === routes.OS_SYNC) {
       this.browserProxy_.didNavigateAwayFromOsSyncPage();
     }
   },
@@ -199,7 +220,7 @@
     if (this.syncStatus.hasUnrecoverableError) {
       return 'sync-problem';
     }
-    if (this.syncStatus.statusAction === settings.StatusAction.REAUTHENTICATE) {
+    if (this.syncStatus.statusAction === StatusAction.REAUTHENTICATE) {
       return 'sync-paused';
     }
     return 'sync-problem';
@@ -250,7 +271,7 @@
   onSyncOnOffButtonClick_() {
     assert(this.syncConsentOptionalEnabled_);
     this.browserProxy_.setOsSyncFeatureEnabled(!this.osSyncFeatureEnabled);
-    settings.recordSettingChange();
+    recordSettingChange();
   },
 
   /**
@@ -338,4 +359,3 @@
         !this.osSyncPrefs.osAppsSynced;
   },
 });
-})();
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.html b/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.html
index afcb074..ffef3a9 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.html
@@ -1,84 +1,69 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared">
+  cr-dialog::part(dialog) {
+    width: fit-content;
+  }
 
-<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html">
-<link rel="import" href="../../settings_shared_css.html">
+  #errorIcon {
+    --iron-icon-height: var(--cr-icon-size);
+    --iron-icon-width: var(--cr-icon-size);
+    --iron-icon-fill-color: var(--cros-icon-color-alert);
+    display: inline-block;
+  }
 
-<dom-module id="settings-pin-autosubmit-dialog">
-  <template>
-    <style include="settings-shared">
-      cr-dialog::part(dialog) {
-        width: fit-content;
-      }
+  #errorDiv {
+    align-items: center;
+    color: var(--cros-text-color-alert);
+    display: flex;
+    flex-direction: row;
+    height: 32px;
+    min-height: 0;
+  }
 
-      #errorIcon {
-        --iron-icon-height: var(--cr-icon-size);
-        --iron-icon-width: var(--cr-icon-size);
-        --iron-icon-fill-color: var(--cros-icon-color-alert);
-        display: inline-block;
-      }
+  /* Hide this using visibility: hidden instead of hidden so that the
+     dialog does not resize when there are no problems to display. */
+  #errorDiv[invisible] {
+    visibility: hidden;
+  }
 
-      #errorDiv {
-        align-items: center;
-        color: var(--cros-text-color-alert);
-        display: flex;
-        flex-direction: row;
-        height: 32px;
-        min-height: 0;
-      }
+  #pinKeyboardDiv {
+    justify-content: center;
+    padding: 0;
+  };
 
-      /* Hide this using visibility: hidden instead of hidden so that the
-         dialog does not resize when there are no problems to display. */
-      #errorDiv[invisible] {
-        visibility: hidden;
-      }
+  #pinKeyboard {
+    --cr-input-placeholder-letter-spacing: normal;
+  }
 
-      #pinKeyboardDiv {
-        justify-content: center;
-        padding: 0;
-      };
-
-      #pinKeyboard {
-        --cr-input-placeholder-letter-spacing: normal;
-      }
-
-      #pinAutosubmitDialogSubtitle {
-        padding-bottom: 16px;
-      }
-    </style>
-    <cr-dialog id="dialog" on-close="close" close-text="$i18n{close}">
-      <div slot="title">$i18n{configurePinConfirmPinTitle}</div>
-      <div slot="body">
-        <div id="pinAutosubmitDialogSubtitle">
-          $i18n{pinAutoSubmitPrompt}
+  #pinAutosubmitDialogSubtitle {
+    padding-bottom: 16px;
+  }
+</style>
+<cr-dialog id="dialog" on-close="close" close-text="$i18n{close}">
+  <div slot="title">$i18n{configurePinConfirmPinTitle}</div>
+  <div slot="body">
+    <div id="pinAutosubmitDialogSubtitle">
+      $i18n{pinAutoSubmitPrompt}
+    </div>
+    <!-- PIN keyboard -->
+    <div id="pinKeyboardDiv" class="settings-box continuation">
+      <pin-keyboard id="pinKeyboard" on-pin-change="onPinChange_"
+          on-submit="onPinSubmit_" value="{{pinValue_}}"
+          disabled="[[requestInProcess_]]"
+          enable-letters>
+        <div id="errorDiv" invisible$="[[!error_]]">
+          <iron-icon id="errorIcon" icon="cr:error-outline"></iron-icon>
+          <span id="errorMessage">[[getErrorMessageString_(error_)]]</span>
         </div>
-        <!-- PIN keyboard -->
-        <div id="pinKeyboardDiv" class="settings-box continuation">
-          <pin-keyboard id="pinKeyboard" on-pin-change="onPinChange_"
-              on-submit="onPinSubmit_" value="{{pinValue_}}"
-              disabled="[[requestInProcess_]]"
-              enable-letters>
-            <div id="errorDiv" invisible$="[[!error_]]">
-              <iron-icon id="errorIcon" icon="cr:error-outline"></iron-icon>
-              <span id="errorMessage">[[getErrorMessageString_(error_)]]</span>
-            </div>
-          </pin-keyboard>
-        </div>
-      </div>
-      <div slot="button-container">
-        <cr-button id="cancelButton" class="cancel-button" on-click="onCancelTap_">
-          $i18n{cancel}
-        </cr-button>
-        <cr-button id="confirmButton" class="action-button" on-click="onPinSubmit_"
-            disabled="[[confirmButtonDisabled_]]">
-          $i18n{confirm}
-        </cr-button>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="pin_autosubmit_dialog.js"></script>
-</dom-module>
+      </pin-keyboard>
+    </div>
+  </div>
+  <div slot="button-container">
+    <cr-button id="cancelButton" class="cancel-button" on-click="onCancelTap_">
+      $i18n{cancel}
+    </cr-button>
+    <cr-button id="confirmButton" class="action-button" on-click="onPinSubmit_"
+        disabled="[[confirmButtonDisabled_]]">
+      $i18n{confirm}
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js
index 7cebb59b..139dbec 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.js
@@ -19,7 +19,18 @@
   PinTooLong: 'pinAutoSubmitLongPinError',
 };
 
+import {Polymer, html} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import '//resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js';
+import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import '//resources/js/assert.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
+import '../../settings_shared_css.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-pin-autosubmit-dialog',
 
   behaviors: [I18nBehavior],
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.html b/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.html
index 9a44fd5..a88ebc1 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.html
@@ -1,89 +1,70 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared">
+  #dialog::part(dialog) {
+    min-width: 500px;
+    width: 500px;
+  }
 
-<link rel="import" href="chrome://resources/cr_elements/cr_lottie/cr_lottie.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="../metrics_recorder.html">
-<link rel="import" href="fingerprint_browser_proxy.html">
-<link rel="import" href="../../i18n_setup.html">
+  #scannerLocation {
+    display: block;
+    height: 205px;
+    margin: auto;
+    padding: 10px 0;
+  }
 
-<dom-module id="settings-setup-fingerprint-dialog">
-  <template>
-    <style include="settings-shared">
-      #dialog::part(dialog) {
-        min-width: 500px;
-        width: 500px;
-      }
+  #scannerLocationLottie {
+    height: 220px;
+    padding: 10px 0;
+  }
 
-      #scannerLocation {
-        display: block;
-        height: 205px;
-        margin: auto;
-        padding: 10px 0;
-      }
+  #messageDiv {
+    height: 20px;
+  }
 
-      #scannerLocationLottie {
-        height: 220px;
-        padding: 10px 0;
-      }
+  /* Use this instead of hidden so that the dialog does not resize when the
+     message appears or disappears. */
+  #messageDiv[invisible] {
+    visibility: hidden;
+  }
 
-      #messageDiv {
-        height: 20px;
-      }
-
-      /* Use this instead of hidden so that the dialog does not resize when the
-         message appears or disappears. */
-      #messageDiv[invisible] {
-        visibility: hidden;
-      }
-
-      #closeButton {
-        margin-inline-start: 5px;
-      }
-    </style>
-    <cr-dialog id="dialog" on-close="close"
-        close-text="$i18n{close}">
-      <div slot="title">$i18n{configureFingerprintTitle}</div>
-      <div slot="body">
-        <div id="messageDiv"
-            invisible$="[[!getInstructionMessage_(step_, problemMessage_)]]"
-            aria-live="polite">
-          <span>[[getInstructionMessage_(step_, problemMessage_)]]</span>
-        </div>
-        <template is="dom-if" if="[[shouldUseLottieAnimation_]]">
-          <div id="scannerLocationLottie"
-            hidden="[[!showScannerLocation_(step_)]]" aria-live="polite"
-            aria-label="$i18n{configureFingerprintScannerStepAriaLabel}">
-            <cr-lottie animation-url="fingerprint_scanner_animation.json" autoplay>
-            </cr-lottie>
-          </div>
-        </template>
-        <template is="dom-if" if="[[!shouldUseLottieAnimation_]]">
-          <img id="scannerLocation" hidden="[[!showScannerLocation_(step_)]]"
-            aria-label="$i18n{configureFingerprintScannerStepAriaLabel}"
-            aria-live="polite"
-            src="fingerprint_scanner_illustration.svg">
-        </template>
-        <cr-fingerprint-progress-arc id="arc" circle-radius="100" autoplay
-            hidden="[[!showArc_(step_)]]">
-        </cr-fingerprint-progress-arc>
+  #closeButton {
+    margin-inline-start: 5px;
+  }
+</style>
+<cr-dialog id="dialog" on-close="close"
+    close-text="$i18n{close}">
+  <div slot="title">$i18n{configureFingerprintTitle}</div>
+  <div slot="body">
+    <div id="messageDiv"
+        invisible$="[[!getInstructionMessage_(step_, problemMessage_)]]"
+        aria-live="polite">
+      <span>[[getInstructionMessage_(step_, problemMessage_)]]</span>
+    </div>
+    <template is="dom-if" if="[[shouldUseLottieAnimation_]]">
+      <div id="scannerLocationLottie"
+        hidden="[[!showScannerLocation_(step_)]]" aria-live="polite"
+        aria-label="$i18n{configureFingerprintScannerStepAriaLabel}">
+        <cr-lottie animation-url="fingerprint_scanner_animation.json" autoplay>
+        </cr-lottie>
       </div>
-      <div slot="button-container">
-        <cr-button id="addAnotherButton" on-click="onAddAnotherFingerprint_"
-            hidden$="[[hideAddAnother_(step_, allowAddAnotherFinger)]]">
-          $i18n{configureFingerprintAddAnotherButton}
-        </cr-button>
-        <cr-button id="closeButton"
-            class$="[[getCloseButtonClass_(step_)]]" on-click="onClose_">
-          [[getCloseButtonText_(step_)]]
-        </cr-button>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="setup_fingerprint_dialog.js"></script>
-</dom-module>
+    </template>
+    <template is="dom-if" if="[[!shouldUseLottieAnimation_]]">
+      <img id="scannerLocation" hidden="[[!showScannerLocation_(step_)]]"
+        aria-label="$i18n{configureFingerprintScannerStepAriaLabel}"
+        aria-live="polite"
+        src="fingerprint_scanner_illustration.svg">
+    </template>
+    <cr-fingerprint-progress-arc id="arc" circle-radius="100" autoplay
+        hidden="[[!showArc_(step_)]]">
+    </cr-fingerprint-progress-arc>
+  </div>
+  <div slot="button-container">
+    <cr-button id="addAnotherButton" on-click="onAddAnotherFingerprint_"
+        hidden$="[[hideAddAnother_(step_, allowAddAnotherFinger)]]">
+      $i18n{configureFingerprintAddAnotherButton}
+    </cr-button>
+    <cr-button id="closeButton"
+        class$="[[getCloseButtonClass_(step_)]]" on-click="onClose_">
+      [[getCloseButtonText_(step_)]]
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js
index 42e3467..f776757a 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.js
@@ -2,342 +2,350 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-cr.define('settings', function() {
-  /**
-   * The steps in the fingerprint setup flow.
-   * @enum {number}
-   */
-  /* #export */ const FingerprintSetupStep = {
-    LOCATE_SCANNER: 1,  // The user needs to locate the scanner.
-    MOVE_FINGER: 2,     // The user needs to move finger around the scanner.
-    READY: 3            // The scanner has read the fingerprint successfully.
-  };
+import '//resources/cr_elements/cr_lottie/cr_lottie.m.js';
+import '//resources/cr_elements/cr_fingerprint/cr_fingerprint_progress_arc.m.js';
+import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import '../../settings_shared_css.js';
 
-  /**
-   * Fingerprint sensor locations corresponding to the FingerprintLocation
-   * enumerators in
-   * /chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h
-   * @enum {number}
-   */
-  /* #export */ const FingerprintLocation = {
-    TABLET_POWER_BUTTON: 0,
-    KEYBOARD_BOTTOM_LEFT: 1,
-    KEYBOARD_BOTTOM_RIGHT: 2,
-    KEYBOARD_TOP_RIGHT: 3,
-  };
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {WebUIListenerBehavior} from '//resources/js/web_ui_listener_behavior.m.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-  /**
-   * The amount of milliseconds after a successful but not completed scan before
-   * a message shows up telling the user to scan their finger again.
-   * @type {number}
-   */
-  const SHOW_TAP_SENSOR_MESSAGE_DELAY_MS = 2000;
+import {loadTimeData} from '../../i18n_setup.js';
+import {recordSettingChange, setUserActionRecorderForTesting} from '../metrics_recorder.m.js';
 
-  Polymer({
-    is: 'settings-setup-fingerprint-dialog',
+import {FingerprintAttempt, FingerprintBrowserProxy, FingerprintBrowserProxyImpl, FingerprintInfo, FingerprintResultType, FingerprintScan} from './fingerprint_browser_proxy.js';
 
-    behaviors: [I18nBehavior, WebUIListenerBehavior],
+/**
+ * The steps in the fingerprint setup flow.
+ * @enum {number}
+ */
+export const FingerprintSetupStep = {
+  LOCATE_SCANNER: 1,  // The user needs to locate the scanner.
+  MOVE_FINGER: 2,     // The user needs to move finger around the scanner.
+  READY: 3            // The scanner has read the fingerprint successfully.
+};
 
-    properties: {
-      /**
-       * Whether add another finger is allowed.
-       * @type {boolean}
-       */
-      allowAddAnotherFinger: {
-        type: Boolean,
-        value: true,
-      },
+/**
+ * Fingerprint sensor locations corresponding to the FingerprintLocation
+ * enumerators in
+ * /chrome/browser/ash/login/quick_unlock/quick_unlock_utils.h
+ * @enum {number}
+ */
+export const FingerprintLocation = {
+  TABLET_POWER_BUTTON: 0,
+  KEYBOARD_BOTTOM_LEFT: 1,
+  KEYBOARD_BOTTOM_RIGHT: 2,
+  KEYBOARD_TOP_RIGHT: 3,
+};
 
-      /**
-       * Authentication token provided by settings-fingerprint-list
-       */
-      authToken: {
-        type: String,
-        value: '',
-      },
+/**
+ * The amount of milliseconds after a successful but not completed scan before
+ * a message shows up telling the user to scan their finger again.
+ * @type {number}
+ */
+const SHOW_TAP_SENSOR_MESSAGE_DELAY_MS = 2000;
 
-      /**
-       * The problem message to display.
-       * @private
-       */
-      problemMessage_: {
-        type: String,
-        value: '',
-      },
+Polymer({
+  _template: html`{__html_template__}`,
+  is: 'settings-setup-fingerprint-dialog',
 
-      /**
-       * The setup phase we are on.
-       * @type {!settings.FingerprintSetupStep}
-       * @private
-       */
-      step_: {type: Number, value: FingerprintSetupStep.LOCATE_SCANNER},
+  behaviors: [I18nBehavior, WebUIListenerBehavior],
 
-      /**
-       * The percentage of completion that has been received during setup.
-       * This is used to approximate the progress of the setup.
-       * The value within [0, 100] represents the percent of enrollment
-       * completion.
-       * @type {number}
-       * @private
-       */
-      percentComplete_: {
-        type: Number,
-        value: 0,
-        observer: 'onProgressChanged_',
-      },
-
-      /**
-       * True if lottie animation file should be used instead of an
-       * illustration.
-       * @type {boolean}
-       * @private
-       */
-      shouldUseLottieAnimation_: {
-        type: Boolean,
-        value() {
-          return loadTimeData.getBoolean('useLottieAnimationForFingerprint');
-        },
-        readOnly: true,
-      }
+  properties: {
+    /**
+     * Whether add another finger is allowed.
+     * @type {boolean}
+     */
+    allowAddAnotherFinger: {
+      type: Boolean,
+      value: true,
     },
 
     /**
-     * A message shows after the user has not scanned a finger during setup.
-     * This is the set timeout id.
+     * Authentication token provided by settings-fingerprint-list
+     */
+    authToken: {
+      type: String,
+      value: '',
+    },
+
+    /**
+     * The problem message to display.
+     * @private
+     */
+    problemMessage_: {
+      type: String,
+      value: '',
+    },
+
+    /**
+     * The setup phase we are on.
+     * @type {!FingerprintSetupStep}
+     * @private
+     */
+    step_: {type: Number, value: FingerprintSetupStep.LOCATE_SCANNER},
+
+    /**
+     * The percentage of completion that has been received during setup.
+     * This is used to approximate the progress of the setup.
+     * The value within [0, 100] represents the percent of enrollment
+     * completion.
      * @type {number}
      * @private
      */
-    tapSensorMessageTimeoutId_: 0,
-
-    /** @private {?settings.FingerprintBrowserProxy}*/
-    browserProxy_: null,
-
-    /** @override */
-    attached() {
-      this.addWebUIListener(
-          'on-fingerprint-scan-received', this.onScanReceived_.bind(this));
-      this.browserProxy_ = settings.FingerprintBrowserProxyImpl.getInstance();
-
-      this.$.arc.reset();
-      this.browserProxy_.startEnroll(this.authToken);
-      this.$.dialog.showModal();
+    percentComplete_: {
+      type: Number,
+      value: 0,
+      observer: 'onProgressChanged_',
     },
 
     /**
-     * Closes the dialog.
-     */
-    close() {
-      if (this.$.dialog.open) {
-        this.$.dialog.close();
-      }
-
-      // Note: Reset resets |step_| back to the default, so handle anything that
-      // checks |step_| before resetting.
-      if (this.step_ !== FingerprintSetupStep.READY) {
-        this.browserProxy_.cancelCurrentEnroll();
-      }
-
-      this.reset_();
-    },
-
-    /** private */
-    clearSensorMessageTimeout_() {
-      if (this.tapSensorMessageTimeoutId_ !== 0) {
-        clearTimeout(this.tapSensorMessageTimeoutId_);
-        this.tapSensorMessageTimeoutId_ = 0;
-      }
-    },
-
-    /**
-     * Resets the dialog to its start state. Call this when the dialog gets
-     * closed.
+     * True if lottie animation file should be used instead of an
+     * illustration.
+     * @type {boolean}
      * @private
      */
-    reset_() {
-      this.step_ = FingerprintSetupStep.LOCATE_SCANNER;
-      this.percentComplete_ = 0;
-      this.clearSensorMessageTimeout_();
-    },
+    shouldUseLottieAnimation_: {
+      type: Boolean,
+      value() {
+        return loadTimeData.getBoolean('useLottieAnimationForFingerprint');
+      },
+      readOnly: true,
+    }
+  },
 
-    /**
-     * Closes the dialog.
-     * @private
-     */
-    onClose_() {
-      if (this.$.dialog.open) {
-        this.$.dialog.close();
-      }
-    },
+  /**
+   * A message shows after the user has not scanned a finger during setup.
+   * This is the set timeout id.
+   * @type {number}
+   * @private
+   */
+  tapSensorMessageTimeoutId_: 0,
 
-    /**
-     * Advances steps, shows problems and animates the progress as needed based
-     * on scan results.
-     * @param {!settings.FingerprintScan} scan
-     * @private
-     */
-    onScanReceived_(scan) {
-      switch (this.step_) {
-        case FingerprintSetupStep.LOCATE_SCANNER:
-          this.$.arc.reset();
-          this.step_ = FingerprintSetupStep.MOVE_FINGER;
-          this.percentComplete_ = scan.percentComplete;
-          this.setProblem_(scan.result);
-          break;
-        case FingerprintSetupStep.MOVE_FINGER:
-          if (scan.isComplete) {
-            this.problemMessage_ = '';
-            this.step_ = FingerprintSetupStep.READY;
-            this.clearSensorMessageTimeout_();
-            this.fire('add-fingerprint');
-          } else {
-            this.setProblem_(scan.result);
-          }
-          this.percentComplete_ = scan.percentComplete;
-          break;
-        case FingerprintSetupStep.READY:
-          break;
-        default:
-          assertNotReached();
-          break;
-      }
-    },
+  /** @private {?FingerprintBrowserProxy}*/
+  browserProxy_: null,
 
-    /**
-     * Sets the instructions based on which phase of the fingerprint setup we
-     * are on.
-     * @param {!settings.FingerprintSetupStep} step The current step the
-     *     fingerprint setup is on.
-     * @param {string} problemMessage Message for the scan result.
-     * @private
-     */
-    getInstructionMessage_(step, problemMessage) {
-      switch (step) {
-        case FingerprintSetupStep.LOCATE_SCANNER:
-          return this.i18n('configureFingerprintInstructionLocateScannerStep');
-        case FingerprintSetupStep.MOVE_FINGER:
-          return problemMessage;
-        case FingerprintSetupStep.READY:
-          return this.i18n('configureFingerprintInstructionReadyStep');
-      }
-      assertNotReached();
-    },
+  /** @override */
+  attached() {
+    this.addWebUIListener(
+        'on-fingerprint-scan-received', this.onScanReceived_.bind(this));
+    this.browserProxy_ = FingerprintBrowserProxyImpl.getInstance();
 
-    /**
-     * Set the problem message based on the result from the fingerprint scanner.
-     * @param {!settings.FingerprintResultType} scanResult The result the
-     *     fingerprint scanner gives.
-     * @private
-     */
-    setProblem_(scanResult) {
-      this.clearSensorMessageTimeout_();
-      switch (scanResult) {
-        case settings.FingerprintResultType.SUCCESS:
-          this.problemMessage_ = '';
-          this.tapSensorMessageTimeoutId_ = setTimeout(() => {
-            this.problemMessage_ = this.i18n('configureFingerprintLiftFinger');
-          }, SHOW_TAP_SENSOR_MESSAGE_DELAY_MS);
-          break;
-        case settings.FingerprintResultType.PARTIAL:
-        case settings.FingerprintResultType.INSUFFICIENT:
-        case settings.FingerprintResultType.SENSOR_DIRTY:
-        case settings.FingerprintResultType.TOO_SLOW:
-        case settings.FingerprintResultType.TOO_FAST:
-          this.problemMessage_ = this.i18n('configureFingerprintTryAgain');
-          break;
-        case settings.FingerprintResultType.IMMOBILE:
-          this.problemMessage_ = this.i18n('configureFingerprintImmobile');
-          break;
-        default:
-          assertNotReached();
-          break;
-      }
-    },
+    this.$.arc.reset();
+    this.browserProxy_.startEnroll(this.authToken);
+    this.$.dialog.showModal();
+  },
 
-    /**
-     * Displays the text of the close button based on which phase of the
-     * fingerprint setup we are on.
-     * @param {!settings.FingerprintSetupStep} step The current step the
-     *     fingerprint setup is on.
-     * @private
-     */
-    getCloseButtonText_(step) {
-      if (step === FingerprintSetupStep.READY) {
-        return this.i18n('done');
-      }
+  /**
+   * Closes the dialog.
+   */
+  close() {
+    if (this.$.dialog.open) {
+      this.$.dialog.close();
+    }
 
-      return this.i18n('cancel');
-    },
+    // Note: Reset resets |step_| back to the default, so handle anything that
+    // checks |step_| before resetting.
+    if (this.step_ !== FingerprintSetupStep.READY) {
+      this.browserProxy_.cancelCurrentEnroll();
+    }
 
-    /**
-     * @param {!settings.FingerprintSetupStep} step
-     * @private
-     */
-    getCloseButtonClass_(step) {
-      if (step === FingerprintSetupStep.READY) {
-        return 'action-button';
-      }
+    this.reset_();
+  },
 
-      return 'cancel-button';
-    },
+  /** private */
+  clearSensorMessageTimeout_() {
+    if (this.tapSensorMessageTimeoutId_ !== 0) {
+      clearTimeout(this.tapSensorMessageTimeoutId_);
+      this.tapSensorMessageTimeoutId_ = 0;
+    }
+  },
 
-    /**
-     * @param {!settings.FingerprintSetupStep} step
-     * @param {boolean} allowAddAnotherFinger
-     * @private
-     */
-    hideAddAnother_(step, allowAddAnotherFinger) {
-      return step !== FingerprintSetupStep.READY || !allowAddAnotherFinger;
-    },
+  /**
+   * Resets the dialog to its start state. Call this when the dialog gets
+   * closed.
+   * @private
+   */
+  reset_() {
+    this.step_ = FingerprintSetupStep.LOCATE_SCANNER;
+    this.percentComplete_ = 0;
+    this.clearSensorMessageTimeout_();
+  },
 
-    /**
-     * Enrolls the finished fingerprint and sets the dialog back to step one to
-     * prepare to enroll another fingerprint.
-     * @private
-     */
-    onAddAnotherFingerprint_() {
-      this.reset_();
-      this.$.arc.reset();
-      this.step_ = FingerprintSetupStep.MOVE_FINGER;
-      this.browserProxy_.startEnroll(this.authToken);
-      settings.recordSettingChange();
-    },
+  /**
+   * Closes the dialog.
+   * @private
+   */
+  onClose_() {
+    if (this.$.dialog.open) {
+      this.$.dialog.close();
+    }
+  },
 
-    /**
-     * Whether scanner location should be shown at the current step.
-     * @private
-     */
-    showScannerLocation_() {
-      return this.step_ === FingerprintSetupStep.LOCATE_SCANNER;
-    },
-
-    /**
-     * Whether fingerprint progress circle should be shown at the current step.
-     * @private
-     */
-    showArc_() {
-      return this.step_ === FingerprintSetupStep.MOVE_FINGER ||
-          this.step_ === FingerprintSetupStep.READY;
-    },
-
-    /**
-     * Observer for percentComplete_.
-     * @private
-     */
-    onProgressChanged_(newValue, oldValue) {
-      // Start a new enrollment, so reset all enrollment related states.
-      if (newValue === 0) {
+  /**
+   * Advances steps, shows problems and animates the progress as needed based
+   * on scan results.
+   * @param {!FingerprintScan} scan
+   * @private
+   */
+  onScanReceived_(scan) {
+    switch (this.step_) {
+      case FingerprintSetupStep.LOCATE_SCANNER:
         this.$.arc.reset();
-        return;
-      }
+        this.step_ = FingerprintSetupStep.MOVE_FINGER;
+        this.percentComplete_ = scan.percentComplete;
+        this.setProblem_(scan.result);
+        break;
+      case FingerprintSetupStep.MOVE_FINGER:
+        if (scan.isComplete) {
+          this.problemMessage_ = '';
+          this.step_ = FingerprintSetupStep.READY;
+          this.clearSensorMessageTimeout_();
+          this.fire('add-fingerprint');
+        } else {
+          this.setProblem_(scan.result);
+        }
+        this.percentComplete_ = scan.percentComplete;
+        break;
+      case FingerprintSetupStep.READY:
+        break;
+      default:
+        assertNotReached();
+        break;
+    }
+  },
 
-      this.$.arc.setProgress(oldValue, newValue, newValue === 100);
-    },
-  });
+  /**
+   * Sets the instructions based on which phase of the fingerprint setup we
+   * are on.
+   * @param {!FingerprintSetupStep} step The current step the
+   *     fingerprint setup is on.
+   * @param {string} problemMessage Message for the scan result.
+   * @private
+   */
+  getInstructionMessage_(step, problemMessage) {
+    switch (step) {
+      case FingerprintSetupStep.LOCATE_SCANNER:
+        return this.i18n('configureFingerprintInstructionLocateScannerStep');
+      case FingerprintSetupStep.MOVE_FINGER:
+        return problemMessage;
+      case FingerprintSetupStep.READY:
+        return this.i18n('configureFingerprintInstructionReadyStep');
+    }
+    assertNotReached();
+  },
 
-  // #cr_define_end
-  return {
-    FingerprintSetupStep,
-    FingerprintLocation,
-    SHOW_TAP_SENSOR_MESSAGE_DELAY_MS
-  };
+  /**
+   * Set the problem message based on the result from the fingerprint scanner.
+   * @param {!FingerprintResultType} scanResult The result the
+   *     fingerprint scanner gives.
+   * @private
+   */
+  setProblem_(scanResult) {
+    this.clearSensorMessageTimeout_();
+    switch (scanResult) {
+      case FingerprintResultType.SUCCESS:
+        this.problemMessage_ = '';
+        this.tapSensorMessageTimeoutId_ = setTimeout(() => {
+          this.problemMessage_ = this.i18n('configureFingerprintLiftFinger');
+        }, SHOW_TAP_SENSOR_MESSAGE_DELAY_MS);
+        break;
+      case FingerprintResultType.PARTIAL:
+      case FingerprintResultType.INSUFFICIENT:
+      case FingerprintResultType.SENSOR_DIRTY:
+      case FingerprintResultType.TOO_SLOW:
+      case FingerprintResultType.TOO_FAST:
+        this.problemMessage_ = this.i18n('configureFingerprintTryAgain');
+        break;
+      case FingerprintResultType.IMMOBILE:
+        this.problemMessage_ = this.i18n('configureFingerprintImmobile');
+        break;
+      default:
+        assertNotReached();
+        break;
+    }
+  },
+
+  /**
+   * Displays the text of the close button based on which phase of the
+   * fingerprint setup we are on.
+   * @param {!FingerprintSetupStep} step The current step the
+   *     fingerprint setup is on.
+   * @private
+   */
+  getCloseButtonText_(step) {
+    if (step === FingerprintSetupStep.READY) {
+      return this.i18n('done');
+    }
+
+    return this.i18n('cancel');
+  },
+
+  /**
+   * @param {!FingerprintSetupStep} step
+   * @private
+   */
+  getCloseButtonClass_(step) {
+    if (step === FingerprintSetupStep.READY) {
+      return 'action-button';
+    }
+
+    return 'cancel-button';
+  },
+
+  /**
+   * @param {!FingerprintSetupStep} step
+   * @param {boolean} allowAddAnotherFinger
+   * @private
+   */
+  hideAddAnother_(step, allowAddAnotherFinger) {
+    return step !== FingerprintSetupStep.READY || !allowAddAnotherFinger;
+  },
+
+  /**
+   * Enrolls the finished fingerprint and sets the dialog back to step one to
+   * prepare to enroll another fingerprint.
+   * @private
+   */
+  onAddAnotherFingerprint_() {
+    this.reset_();
+    this.$.arc.reset();
+    this.step_ = FingerprintSetupStep.MOVE_FINGER;
+    this.browserProxy_.startEnroll(this.authToken);
+    recordSettingChange();
+  },
+
+  /**
+   * Whether scanner location should be shown at the current step.
+   * @private
+   */
+  showScannerLocation_() {
+    return this.step_ === FingerprintSetupStep.LOCATE_SCANNER;
+  },
+
+  /**
+   * Whether fingerprint progress circle should be shown at the current step.
+   * @private
+   */
+  showArc_() {
+    return this.step_ === FingerprintSetupStep.MOVE_FINGER ||
+        this.step_ === FingerprintSetupStep.READY;
+  },
+
+  /**
+   * Observer for percentComplete_.
+   * @private
+   */
+  onProgressChanged_(newValue, oldValue) {
+    // Start a new enrollment, so reset all enrollment related states.
+    if (newValue === 0) {
+      this.$.arc.reset();
+      return;
+    }
+
+    this.$.arc.setProgress(oldValue, newValue, newValue === 100);
+  },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.html b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.html
index 2bd4b5c8..b8ff8b6 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.html
@@ -1,52 +1,37 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared">
+  #pinKeyboardDiv {
+    justify-content: center;
+  };
 
-<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="../../i18n_setup.html">
-<link rel="import" href="../../settings_shared_css.html">
-
-<dom-module id="settings-setup-pin-dialog">
-  <template>
-    <style include="settings-shared">
-      #pinKeyboardDiv {
-        justify-content: center;
-      };
-
-      #pinKeyboard {
-        --cr-input-placeholder-letter-spacing: normal;
-      }
-    </style>
-    <cr-dialog id="dialog" on-close="close"
-        close-text="$i18n{close}">
-      <div slot="title">[[getTitleMessage_(isConfirmStep_)]]</div>
-      <div slot="body">
-        <!-- PIN keyboard -->
-        <div id="pinKeyboardDiv" class="settings-box continuation">
-          <setup-pin-keyboard id="pinKeyboard"
-              enable-submit="{{enableSubmit_}}"
-              is-confirm-step="{{isConfirmStep_}}"
-              on-pin-submit="onPinSubmit_"
-              on-set-pin-done="onSetPinDone_"
-              set-modes="{{setModes}}"
-              quick-unlock-private="[[quickUnlockPrivate]]"
-              write-uma="[[writeUma_]]"
-              enable-placeholder>
-          </setup-pin-keyboard>
-        </div>
-      </div>
-      <div slot="button-container">
-        <cr-button class="cancel-button" on-click="onCancelTap_">
-          $i18n{cancel}
-        </cr-button>
-        <cr-button class="action-button" on-click="onPinSubmit_"
-            disabled$="[[!enableSubmit_]]">
-          <span>[[getContinueMessage_(isConfirmStep_)]]</span>
-        </cr-button>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="setup_pin_dialog.js"></script>
-</dom-module>
+  #pinKeyboard {
+    --cr-input-placeholder-letter-spacing: normal;
+  }
+</style>
+<cr-dialog id="dialog" on-close="close"
+    close-text="$i18n{close}">
+  <div slot="title">[[getTitleMessage_(isConfirmStep_)]]</div>
+  <div slot="body">
+    <!-- PIN keyboard -->
+    <div id="pinKeyboardDiv" class="settings-box continuation">
+      <setup-pin-keyboard id="pinKeyboard"
+          enable-submit="{{enableSubmit_}}"
+          is-confirm-step="{{isConfirmStep_}}"
+          on-pin-submit="onPinSubmit_"
+          on-set-pin-done="onSetPinDone_"
+          set-modes="{{setModes}}"
+          quick-unlock-private="[[quickUnlockPrivate]]"
+          write-uma="[[writeUma_]]"
+          enable-placeholder>
+      </setup-pin-keyboard>
+    </div>
+  </div>
+  <div slot="button-container">
+    <cr-button class="cancel-button" on-click="onCancelTap_">
+      $i18n{cancel}
+    </cr-button>
+    <cr-button class="action-button" on-click="onPinSubmit_"
+        disabled$="[[!enableSubmit_]]">
+      <span>[[getContinueMessage_(isConfirmStep_)]]</span>
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
index cf48e3e..06e1203 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.js
@@ -11,7 +11,19 @@
  * </settings-setup-pin-dialog>
  */
 
+import '//resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.m.js';
+import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import '../../settings_shared_css.js';
+
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {loadTimeData} from '../../i18n_setup.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-setup-pin-dialog',
 
   behaviors: [I18nBehavior],
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/user_list.html b/chrome/browser/resources/settings/chromeos/os_people_page/user_list.html
index 2168c325..d080721 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/user_list.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/user_list.html
@@ -1,76 +1,56 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared iron-flex">
+  .user-list {
+    /* 4 users (the extra 1px is to account for the border-bottom) */
+    max-height: calc(4 * (var(--settings-row-two-line-min-height) + 1px));
+    overflow-y: auto;
+  }
 
-<link rel="import" href="chrome://resources/cr_elements/cr_icon_button/cr_icon_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_scrollable_behavior.html">
-<link rel="import" href="chrome://resources/cr_elements/icons.html">
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html">
-<link rel="import" href="../os_route.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../route_observer_behavior.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="../../settings_vars_css.html">
+  .user {
+    border-bottom: var(--cr-separator-line);
+  }
 
-<dom-module id="settings-user-list">
-  <template>
-    <style include="settings-shared iron-flex">
-      .user-list {
-        /* 4 users (the extra 1px is to account for the border-bottom) */
-        max-height: calc(4 * (var(--settings-row-two-line-min-height) + 1px));
-        overflow-y: auto;
-      }
+  .user-icon {
+    background-position: center;
+    background-repeat: no-repeat;
+    background-size: cover;
+    border-radius: 20px;
+    flex-shrink: 0;
+    height: 40px;
+    width: 40px;
+  }
 
-      .user {
-        border-bottom: var(--cr-separator-line);
-      }
+  .user-info {
+    padding-inline-start: 20px;
+  }
 
-      .user-icon {
-        background-position: center;
-        background-repeat: no-repeat;
-        background-size: cover;
-        border-radius: 20px;
-        flex-shrink: 0;
-        height: 40px;
-        width: 40px;
-      }
-
-      .user-info {
-        padding-inline-start: 20px;
-      }
-
-      :host([disabled]) .user-list {
-        opacity: var(--cr-disabled-opacity);
-        overflow: auto;
-      }
-    </style>
-    <div class="user-list" scrollable>
-      <template is="dom-repeat" items="[[users_]]">
-        <div class="user layout horizontal center two-line">
-          <img class="user-icon" src="[[getProfilePictureUrl_(item)]]"
-              aria-hidden="true">
-          <div class="flex layout vertical user-info no-min-width">
-            <div class="text-elide" title="[[getTooltip_(item)]]"
-                aria-describedby="userEmail-[[index]]">
-              [[getUserName_(item)]]
-            </div>
-            <div class="secondary text-elide" id="userEmail-[[index]]"
-                title="[[item.displayEmail]]"
-                hidden$="[[!shouldShowEmail_(item)]]"
-                aria-hidden="true">
-              [[item.displayEmail]]
-            </div>
-          </div>
-          <template is="dom-if"
-              if="[[!shouldHideCloseButton_(disabled, item.isOwner)]]">
-            <cr-icon-button class="icon-clear"
-                title="[[getRemoveUserTooltip_(item)]]" on-click="removeUser_">
-            </cr-icon-button>
-          </template>
+  :host([disabled]) .user-list {
+    opacity: var(--cr-disabled-opacity);
+    overflow: auto;
+  }
+</style>
+<div class="user-list" scrollable>
+  <template is="dom-repeat" items="[[users_]]">
+    <div class="user layout horizontal center two-line">
+      <img class="user-icon" src="[[getProfilePictureUrl_(item)]]"
+          aria-hidden="true">
+      <div class="flex layout vertical user-info no-min-width">
+        <div class="text-elide" title="[[getTooltip_(item)]]"
+            aria-describedby="userEmail-[[index]]">
+          [[getUserName_(item)]]
         </div>
+        <div class="secondary text-elide" id="userEmail-[[index]]"
+            title="[[item.displayEmail]]"
+            hidden$="[[!shouldShowEmail_(item)]]"
+            aria-hidden="true">
+          [[item.displayEmail]]
+        </div>
+      </div>
+      <template is="dom-if"
+          if="[[!shouldHideCloseButton_(disabled, item.isOwner)]]">
+        <cr-icon-button class="icon-clear"
+            title="[[getRemoveUserTooltip_(item)]]" on-click="removeUser_">
+        </cr-icon-button>
       </template>
     </div>
   </template>
-  <script src="user_list.js"></script>
-</dom-module>
+</div>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js b/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js
index 9f06635..d9e493b6 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/user_list.js
@@ -12,13 +12,30 @@
  *    <settings-user-list prefs="{{prefs}}">
  *    </settings-user-list>
  */
+import '//resources/cr_elements/cr_icon_button/cr_icon_button.m.js';
+import '//resources/cr_elements/icons.m.js';
+import '//resources/cr_elements/shared_vars_css.m.js';
+import '//resources/polymer/v3_0/iron-flex-layout/iron-flex-layout-classes.js';
+import '../../settings_shared_css.js';
+import '../../settings_vars_css.js';
+
+import {CrScrollableBehavior} from '//resources/cr_elements/cr_scrollable_behavior.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {IronA11yAnnouncer} from '//resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {Route, Router} from '../../router.js';
+import {routes} from '../os_route.m.js';
+import {RouteObserverBehavior} from '../route_observer_behavior.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-user-list',
 
   behaviors: [
     CrScrollableBehavior,
     I18nBehavior,
-    settings.RouteObserverBehavior,
+    RouteObserverBehavior,
   ],
 
   properties: {
@@ -52,7 +69,7 @@
   /** @override */
   attached() {
     // Initialize the announcer once.
-    Polymer.IronA11yAnnouncer.requestAvailability();
+    IronA11yAnnouncer.requestAvailability();
   },
 
   /** @override */
@@ -71,8 +88,7 @@
 
   /** @protected */
   currentRouteChanged() {
-    if (settings.Router.getInstance().getCurrentRoute() ===
-        settings.routes.ACCOUNTS) {
+    if (Router.getInstance().getCurrentRoute() === routes.ACCOUNTS) {
       this.usersPrivate_.getUsers(
           (/** !Array<!chrome.usersPrivate.User> */ users) => {
             this.setUsers_(users);
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.html b/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.html
index 6a6e745..a0cc85e 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.html
@@ -1,40 +1,24 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
-
-<link rel="import" href="chrome://resources/cr_elements/cr_button/cr_button.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html">
-<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html">
-<link rel="import" href="chrome://resources/polymer/v1_0/iron-a11y-announcer/iron-a11y-announcer.html">
-<link rel="import" href="chrome://resources/html/i18n_behavior.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="../../settings_vars_css.html">
-
-<dom-module id="settings-users-add-user-dialog">
-  <template>
-    <style include="settings-shared">
-      cr-dialog::part(dialog) {
-        width: 320px;
-      }
-    </style>
-    <cr-dialog id="dialog" close-text="$i18n{close}">
-      <div slot="title">$i18n{addUsers}</div>
-      <div slot="body">
-        <cr-input id="addUserInput" label="$i18n{addUsersEmail}"
-            invalid="[[shouldShowError_(errorCode_)]]"
-            on-value-changed="onInput_"
-            error-message="[[getErrorString_(errorCode_)]]" autofocus>
-        </cr-input>
-      </div>
-      <div slot="button-container">
-        <cr-button class="cancel-button" on-click="onCancelTap_">
-          $i18n{cancel}
-        </cr-button>
-        <cr-button on-click="addUser_" class="action-button"
-            disabled$="[[!canAddUser_(isEmail_, isEmpty_)]]">
-          $i18n{add}
-        </cr-button>
-      </div>
-    </cr-dialog>
-  </template>
-  <script src="users_add_user_dialog.js"></script>
-</dom-module>
+<style include="settings-shared">
+  cr-dialog::part(dialog) {
+    width: 320px;
+  }
+</style>
+<cr-dialog id="dialog" close-text="$i18n{close}">
+  <div slot="title">$i18n{addUsers}</div>
+  <div slot="body">
+    <cr-input id="addUserInput" label="$i18n{addUsersEmail}"
+        invalid="[[shouldShowError_(errorCode_)]]"
+        on-value-changed="onInput_"
+        error-message="[[getErrorString_(errorCode_)]]" autofocus>
+    </cr-input>
+  </div>
+  <div slot="button-container">
+    <cr-button class="cancel-button" on-click="onCancelTap_">
+      $i18n{cancel}
+    </cr-button>
+    <cr-button on-click="addUser_" class="action-button"
+        disabled$="[[!canAddUser_(isEmail_, isEmpty_)]]">
+      $i18n{add}
+    </cr-button>
+  </div>
+</cr-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.js b/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.js
index ca54228d..32e2c6a 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.js
@@ -7,7 +7,17 @@
  * 'settings-users-add-user-dialog' is the dialog shown for adding new allowed
  * users to a ChromeOS device.
  */
-(function() {
+import '//resources/cr_elements/cr_button/cr_button.m.js';
+import '//resources/cr_elements/cr_dialog/cr_dialog.m.js';
+import '//resources/cr_elements/cr_input/cr_input.m.js';
+import '../../settings_shared_css.js';
+import '../../settings_vars_css.js';
+
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {I18nBehavior} from '//resources/js/i18n_behavior.m.js';
+import {IronA11yAnnouncer} from '//resources/polymer/v3_0/iron-a11y-announcer/iron-a11y-announcer.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
 
 /**
  * Regular expression for adding a user where the string provided is just
@@ -37,6 +47,7 @@
 };
 
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-users-add-user-dialog',
 
   behaviors: [I18nBehavior],
@@ -66,7 +77,7 @@
   /** @override */
   attached() {
     // Initialize the announcer once.
-    Polymer.IronA11yAnnouncer.requestAvailability();
+    IronA11yAnnouncer.requestAvailability();
   },
 
   open() {
@@ -164,4 +175,3 @@
     return '';
   },
 });
-})();
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/users_page.html b/chrome/browser/resources/settings/chromeos/os_people_page/users_page.html
index d8d189c..dbe369b0 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/users_page.html
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/users_page.html
@@ -1,88 +1,66 @@
-<link rel="import" href="chrome://resources/html/polymer.html">
+<style include="settings-shared action-link">
+  .settings-box:first-of-type {
+    border-top: none;
+  }
 
-<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html">
-<link rel="import" href="chrome://resources/cr_elements/action_link_css.html">
-<link rel="import" href="chrome://resources/html/action_link.html">
-<link rel="import" href="chrome://resources/html/assert.html">
-<link rel="import" href="chrome://resources/html/cr/ui/focus_without_ink.html">
-<link rel="import" href="../../controls/settings_toggle_button.html">
-<link rel="import" href="../../settings_shared_css.html">
-<link rel="import" href="user_list.html">
-<link rel="import" href="users_add_user_dialog.html">
-<link rel="import" href="../deep_linking_behavior.html">
-<link rel="import" href="../os_route.html">
-<link rel="import" href="../../router.html">
-<link rel="import" href="../route_observer_behavior.html">
-<link rel="import" href="../../i18n_setup.html">
+  #add-user-button {
+    /* Add user button must be lined up with the start of users' names. */
+    margin-inline-start: var(--cr-section-indent-padding);
+  }
 
-<dom-module id="settings-users-page">
-  <template>
-    <style include="settings-shared action-link">
-      .settings-box:first-of-type {
-        border-top: none;
-      }
+  .block {
+    display: block;
+  }
 
-      #add-user-button {
-        /* Add user button must be lined up with the start of users' names. */
-        margin-inline-start: var(--cr-section-indent-padding);
-      }
-
-      .block {
-        display: block;
-      }
-
-      #header {
-        padding-inline-start: 20px;
-      }
-    </style>
-    <div class="settings-box" hidden$="[[!isUserListManaged_]]">
-      $i18n{settingsManagedLabel}
+  #header {
+    padding-inline-start: 20px;
+  }
+</style>
+<div class="settings-box" hidden$="[[!isUserListManaged_]]">
+  $i18n{settingsManagedLabel}
+</div>
+<div class="settings-box"
+    hidden$="[[shouldHideModifiedByOwnerLabel_(
+        isUserListManaged_, isOwner_)]]">
+  $i18n{usersModifiedByOwnerLabel}
+</div>
+<settings-toggle-button
+    pref="{{prefs.cros.accounts.allowBWSI}}"
+    id="allowGuestBrowsing"
+    label="$i18n{guestBrowsingLabel}"
+    disabled="[[isEditingDisabled_(isOwner_, isUserListManaged_)]]"
+    deep-link-focus-id$="[[Setting.kGuestBrowsingV2]]">
+</settings-toggle-button>
+<settings-toggle-button
+    pref="{{prefs.cros.accounts.showUserNamesOnSignIn}}"
+    id="showUserNamesOnSignIn"
+    label="$i18n{showOnSigninLabel}"
+    disabled="[[isEditingDisabled_(isOwner_, isUserListManaged_)]]"
+    deep-link-focus-id$="[[Setting.kShowUsernamesAndPhotosAtSignInV2]]">
+</settings-toggle-button>
+<settings-toggle-button
+    pref="{{prefs.cros.accounts.allowGuest}}"
+    id="restrictSignIn"
+    label="$i18n{restrictSigninLabel}"
+    disabled="[[isEditingDisabled_(isOwner_, isUserListManaged_)]]"
+    deep-link-focus-id$="[[Setting.kRestrictSignInV2]]"
+    inverted>
+</settings-toggle-button>
+<template is="dom-if"
+    if="[[isEditingUsersEnabled_(isOwner_, isUserListManaged_,
+        prefs.cros.accounts.allowGuest.value, isChild_)]]">
+  <div class="list-frame" >
+    <settings-user-list prefs="[[prefs]]">
+    </settings-user-list>
+    <div id="add-user-button" class="list-item">
+      <a is="action-link"
+          deep-link-focus-id$="[[Setting.kAddToUserAllowlistV2]]"
+          class="list-button" on-click="openAddUserDialog_">
+        $i18n{addUsers}
+      </a>
     </div>
-    <div class="settings-box"
-        hidden$="[[shouldHideModifiedByOwnerLabel_(
-            isUserListManaged_, isOwner_)]]">
-      $i18n{usersModifiedByOwnerLabel}
-    </div>
-    <settings-toggle-button
-        pref="{{prefs.cros.accounts.allowBWSI}}"
-        id="allowGuestBrowsing"
-        label="$i18n{guestBrowsingLabel}"
-        disabled="[[isEditingDisabled_(isOwner_, isUserListManaged_)]]"
-        deep-link-focus-id$="[[Setting.kGuestBrowsingV2]]">
-    </settings-toggle-button>
-    <settings-toggle-button
-        pref="{{prefs.cros.accounts.showUserNamesOnSignIn}}"
-        id="showUserNamesOnSignIn"
-        label="$i18n{showOnSigninLabel}"
-        disabled="[[isEditingDisabled_(isOwner_, isUserListManaged_)]]"
-        deep-link-focus-id$="[[Setting.kShowUsernamesAndPhotosAtSignInV2]]">
-    </settings-toggle-button>
-    <settings-toggle-button
-        pref="{{prefs.cros.accounts.allowGuest}}"
-        id="restrictSignIn"
-        label="$i18n{restrictSigninLabel}"
-        disabled="[[isEditingDisabled_(isOwner_, isUserListManaged_)]]"
-        deep-link-focus-id$="[[Setting.kRestrictSignInV2]]"
-        inverted>
-    </settings-toggle-button>
-    <template is="dom-if"
-        if="[[isEditingUsersEnabled_(isOwner_, isUserListManaged_,
-            prefs.cros.accounts.allowGuest.value, isChild_)]]">
-      <div class="list-frame" >
-        <settings-user-list prefs="[[prefs]]">
-        </settings-user-list>
-        <div id="add-user-button" class="list-item">
-          <a is="action-link"
-              deep-link-focus-id$="[[Setting.kAddToUserAllowlistV2]]"
-              class="list-button" on-click="openAddUserDialog_">
-            $i18n{addUsers}
-          </a>
-        </div>
-      </div>
-    </template>
-    <settings-users-add-user-dialog id="addUserDialog"
-        on-close="onAddUserDialogClose_">
-    </settings-users-add-user-dialog>
-  </template>
-  <script src="users_page.js"></script>
-</dom-module>
+  </div>
+</template>
+<settings-users-add-user-dialog id="addUserDialog"
+    on-close="onAddUserDialogClose_">
+</settings-users-add-user-dialog>
diff --git a/chrome/browser/resources/settings/chromeos/os_people_page/users_page.js b/chrome/browser/resources/settings/chromeos/os_people_page/users_page.js
index f0b209c..68cf2df 100644
--- a/chrome/browser/resources/settings/chromeos/os_people_page/users_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_people_page/users_page.js
@@ -7,12 +7,31 @@
  * 'settings-users-page' is the settings page for managing user accounts on
  * the device.
  */
+import '//resources/cr_elements/shared_vars_css.m.js';
+import '//resources/cr_elements/action_link_css.m.js';
+import '//resources/js/action_link.js';
+import '../../controls/settings_toggle_button.js';
+import '../../settings_shared_css.js';
+import './user_list.js';
+import './users_add_user_dialog.js';
+
+import {assert, assertNotReached} from '//resources/js/assert.m.js';
+import {focusWithoutInk} from '//resources/js/cr/ui/focus_without_ink.m.js';
+import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+
+import {loadTimeData} from '../../i18n_setup.js';
+import {Route, Router} from '../../router.js';
+import {DeepLinkingBehavior} from '../deep_linking_behavior.m.js';
+import {routes} from '../os_route.m.js';
+import {RouteObserverBehavior} from '../route_observer_behavior.js';
+
 Polymer({
+  _template: html`{__html_template__}`,
   is: 'settings-users-page',
 
   behaviors: [
     DeepLinkingBehavior,
-    settings.RouteObserverBehavior,
+    RouteObserverBehavior,
   ],
 
   properties: {
@@ -86,7 +105,7 @@
     }
 
     // Wait for element to load.
-    Polymer.RenderStatus.afterNextRender(this, () => {
+    afterNextRender(this, () => {
       const userList = this.$$('settings-user-list');
       const removeButton = userList.$$('cr-icon-button');
       if (removeButton) {
@@ -100,14 +119,14 @@
   },
 
   /**
-   * settings.RouteObserverBehavior
-   * @param {!settings.Route} route
-   * @param {!settings.Route} oldRoute
+   * RouteObserverBehavior
+   * @param {!Route} route
+   * @param {!Route} oldRoute
    * @protected
    */
   currentRouteChanged(route, oldRoute) {
     // Does not apply to this page.
-    if (route !== settings.routes.ACCOUNTS) {
+    if (route !== routes.ACCOUNTS) {
       return;
     }
 
@@ -157,6 +176,6 @@
 
   /** @private */
   focusAddUserButton_() {
-    cr.ui.focusWithoutInk(assert(this.$$('#add-user-button a')));
+    focusWithoutInk(assert(this.$$('#add-user-button a')));
   },
 });
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn
index 14ff8c9..b9009130 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/BUILD.gn
@@ -25,7 +25,7 @@
     "..:os_route.m",
     "..:route_observer_behavior",
     "../..:router",
-    "../os_people_page:lock_screen.m",
+    "../os_people_page:lock_screen",
     "../os_people_page:lock_state_behavior.m",
     "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
     "//ui/webui/resources/js:load_time_data.m",
diff --git a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
index a4830ad..d1139eb7 100644
--- a/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_privacy_page/os_privacy_page.js
@@ -16,10 +16,10 @@
 import '../../controls/settings_toggle_button.js';
 import '../../settings_shared_css.js';
 import '../../settings_page/settings_subpage.js';
-import '../os_people_page/users_page.m.js';
+import '../os_people_page/users_page.js';
 import '../../settings_page/settings_animated_pages.js';
-import '../os_people_page/lock_screen.m.js';
-import '../os_people_page/lock_screen_password_prompt_dialog.m.js';
+import '../os_people_page/lock_screen.js';
+import '../os_people_page/lock_screen_password_prompt_dialog.js';
 
 import {loadTimeData} from '//resources/js/load_time_data.m.js';
 import {afterNextRender, flush, html, Polymer, TemplateInstanceBase, Templatizer} from '//resources/polymer/v3_0/polymer/polymer_bundled.min.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.gni b/chrome/browser/resources/settings/chromeos/os_settings.gni
index c0615ddf..23ffef7 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.gni
+++ b/chrome/browser/resources/settings/chromeos/os_settings.gni
@@ -456,6 +456,17 @@
                                  "chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_page_v2.html",
                                  "chrome/browser/resources/settings/chromeos/os_languages_page/os_languages_section.html",
                                  "chrome/browser/resources/settings/chromeos/os_languages_page/smart_inputs_page.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/account_manager.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/fingerprint_list.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/lock_screen_password_prompt_dialog.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/os_people_page.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/os_sync_controls.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/pin_autosubmit_dialog.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/setup_fingerprint_dialog.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/setup_pin_dialog.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/user_list.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/users_add_user_dialog.html",
+                                 "chrome/browser/resources/settings/chromeos/os_people_page/users_page.html",
                                  "chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_print_server_dialog.html",
                                  "chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_dialog.html",
                                  "chrome/browser/resources/settings/chromeos/os_printing_page/cups_add_printer_manually_dialog.html",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings.js b/chrome/browser/resources/settings/chromeos/os_settings.js
index ed985af..9300f5d5 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings.js
@@ -59,7 +59,7 @@
 import './os_apps_page/app_management_page/pin_to_shelf_item.js';
 import './os_apps_page/app_management_page/plugin_vm_page/plugin_vm_detail_view.js';
 import './os_apps_page/app_management_page/pwa_detail_view.js';
-import '//resources/cr_components/app_management/shared_style.js';
+import './os_apps_page/app_management_page/shared_style.js';
 import '//resources/cr_components/app_management/shared_vars.js';
 import './os_apps_page/app_management_page/supported_links_overlapping_apps_dialog.js';
 import './os_apps_page/app_management_page/supported_links_dialog.js';
@@ -77,9 +77,9 @@
 import './os_bluetooth_page/os_paired_bluetooth_list.js';
 import './os_bluetooth_page/os_paired_bluetooth_list_item.js';
 import './os_icons.m.js';
-import './os_people_page/account_manager.m.js';
-import './os_people_page/os_people_page.m.js';
-import './os_people_page/os_sync_controls.m.js';
+import './os_people_page/account_manager.js';
+import './os_people_page/os_people_page.js';
+import './os_people_page/os_sync_controls.js';
 import './os_search_page/os_search_page.js';
 import './os_settings_main/os_settings_main.js';
 import './os_settings_page/os_settings_page.js';
@@ -141,9 +141,9 @@
 export {setAppNotificationProviderForTesting} from './os_apps_page/app_notifications_page/mojo_interface_provider.js';
 export {osPageVisibility} from './os_page_visibility.m.js';
 export {AccountManagerBrowserProxy, AccountManagerBrowserProxyImpl} from './os_people_page/account_manager_browser_proxy.js';
-export {FingerprintBrowserProxyImpl, FingerprintResultType} from './os_people_page/fingerprint_browser_proxy.m.js';
+export {FingerprintBrowserProxyImpl, FingerprintResultType} from './os_people_page/fingerprint_browser_proxy.js';
 export {OsSyncBrowserProxyImpl} from './os_people_page/os_sync_browser_proxy.m.js';
-export {FingerprintLocation, FingerprintSetupStep} from './os_people_page/setup_fingerprint_dialog.m.js';
+export {FingerprintLocation, FingerprintSetupStep} from './os_people_page/setup_fingerprint_dialog.js';
 export {MetricsConsentBrowserProxy, MetricsConsentBrowserProxyImpl, MetricsConsentState} from './os_privacy_page/metrics_consent_browser_proxy.js';
 export {DataAccessPolicyState, PeripheralDataAccessBrowserProxy, PeripheralDataAccessBrowserProxyImpl} from './os_privacy_page/peripheral_data_access_browser_proxy.js';
 export {OsResetBrowserProxyImpl} from './os_reset_page/os_reset_browser_proxy.js';
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/BUILD.gn b/chrome/browser/resources/settings/chromeos/os_settings_page/BUILD.gn
index 17a96d04..ef1930a 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/BUILD.gn
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/BUILD.gn
@@ -34,7 +34,7 @@
     "../os_apps_page:os_apps_page",
     "../os_apps_page/app_notifications_page:app_notifications_subpage",
     "../os_bluetooth_page:os_bluetooth_page",
-    "../os_people_page:os_people_page.m",
+    "../os_people_page:os_people_page",
     "../os_printing_page:os_printing_page",
     "../os_privacy_page:os_privacy_page",
     "../os_search_page:os_search_page",
diff --git a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
index f162ed8..35f9100d 100644
--- a/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
+++ b/chrome/browser/resources/settings/chromeos/os_settings_page/os_settings_page.js
@@ -14,7 +14,7 @@
 import '//resources/polymer/v3_0/iron-icon/iron-icon.js';
 import './settings_idle_load.js';
 import '../os_apps_page/os_apps_page.js';
-import '../os_people_page/os_people_page.m.js';
+import '../os_people_page/os_people_page.js';
 import '../os_privacy_page/os_privacy_page.js';
 import '../os_printing_page/os_printing_page.js';
 import '../os_search_page/os_search_page.js';
diff --git a/chrome/browser/resources/tools/rollup_plugin.js b/chrome/browser/resources/tools/rollup_plugin.js
index 345d743..1717bb4 100644
--- a/chrome/browser/resources/tools/rollup_plugin.js
+++ b/chrome/browser/resources/tools/rollup_plugin.js
@@ -62,7 +62,6 @@
   }
 
   const fullUrl = new URL(pathFromUrl, urlPrefix);
-  console.log(fullUrl.href);
   if (excludes.includes(fullUrl.href)) {
     return fullUrl.href;
   }
diff --git a/chrome/browser/storage/storage_notification_service_impl.cc b/chrome/browser/storage/storage_notification_service_impl.cc
index 62c1950..f37e0e7 100644
--- a/chrome/browser/storage/storage_notification_service_impl.cc
+++ b/chrome/browser/storage/storage_notification_service_impl.cc
@@ -10,6 +10,8 @@
 #include "build/build_config.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/common/chrome_switches.h"
+#include "content/public/browser/browser_task_traits.h"
+#include "content/public/browser/browser_thread.h"
 
 #if !defined(OS_ANDROID)
 #include "chrome/browser/ui/storage_pressure_bubble.h"
@@ -35,8 +37,26 @@
 
 }  // namespace
 
+StoragePressureNotificationCallback
+StorageNotificationServiceImpl::CreateThreadSafePressureNotificationCallback() {
+  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+  auto thread_unsafe_callback = base::BindRepeating(
+      &StorageNotificationServiceImpl::MaybeShowStoragePressureNotification,
+      weak_ptr_factory_.GetWeakPtr());
+  return base::BindRepeating(
+      [](StoragePressureNotificationCallback cb, blink::StorageKey key) {
+        content::GetUIThreadTaskRunner({})->PostTask(
+            FROM_HERE,
+            base::BindOnce([](StoragePressureNotificationCallback callback,
+                              blink::StorageKey key) { callback.Run(key); },
+                           std::move(cb), key));
+      },
+      std::move(thread_unsafe_callback));
+}
+
 void StorageNotificationServiceImpl::MaybeShowStoragePressureNotification(
-    const url::Origin origin) {
+    const blink::StorageKey storage_key) {
+  auto origin = storage_key.origin();
   if (!disk_pressure_notification_last_sent_at_.is_null() &&
       base::TimeTicks::Now() - disk_pressure_notification_last_sent_at_ <
           GetThrottlingInterval()) {
diff --git a/chrome/browser/storage/storage_notification_service_impl.h b/chrome/browser/storage/storage_notification_service_impl.h
index 497a306..44fe169b 100644
--- a/chrome/browser/storage/storage_notification_service_impl.h
+++ b/chrome/browser/storage/storage_notification_service_impl.h
@@ -5,10 +5,12 @@
 #ifndef CHROME_BROWSER_STORAGE_STORAGE_NOTIFICATION_SERVICE_IMPL_H_
 #define CHROME_BROWSER_STORAGE_STORAGE_NOTIFICATION_SERVICE_IMPL_H_
 
+#include "base/memory/weak_ptr.h"
 #include "base/time/time.h"
 #include "chrome/browser/profiles/profile.h"
 #include "components/keyed_service/core/keyed_service.h"
 #include "content/public/browser/storage_notification_service.h"
+#include "third_party/blink/public/common/storage_key/storage_key.h"
 
 class StorageNotificationServiceImpl
     : public content::StorageNotificationService,
@@ -16,13 +18,20 @@
  public:
   StorageNotificationServiceImpl();
   ~StorageNotificationServiceImpl() override;
-  void MaybeShowStoragePressureNotification(const url::Origin) override;
+  // Called from the UI thread, this method returns a callback that can passed
+  // to any thread, and proxies calls to
+  // `MaybeShowStoragePressureNotification()` back to the UI thread. It wraps a
+  // weak pointer to `this`.
+  StoragePressureNotificationCallback
+  CreateThreadSafePressureNotificationCallback() override;
+  void MaybeShowStoragePressureNotification(const blink::StorageKey) override;
   base::TimeTicks GetLastSentAtForTesting() {
     return disk_pressure_notification_last_sent_at_;
   }
 
  private:
   base::TimeTicks disk_pressure_notification_last_sent_at_;
+  base::WeakPtrFactory<StorageNotificationServiceImpl> weak_ptr_factory_{this};
 };
 
 #endif  // CHROME_BROWSER_STORAGE_STORAGE_NOTIFICATION_SERVICE_IMPL_H_
diff --git a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
index 004f2d0..fb8133c 100644
--- a/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
+++ b/chrome/browser/ui/android/omnibox/java/src/org/chromium/chrome/browser/omnibox/suggestions/answer/AnswerSuggestionProcessor.java
@@ -173,11 +173,9 @@
                 case AnswerType.SPORTS:
                     return R.drawable.ic_google_round;
                 default:
-                    assert false : "Unsupported answer type";
                     break;
             }
-        } else {
-            assert suggestion.getType() == OmniboxSuggestionType.CALCULATOR;
+        } else if (suggestion.getType() == OmniboxSuggestionType.CALCULATOR) {
             return R.drawable.ic_equals_sign_round;
         }
         return R.drawable.ic_google_round;
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 1eeed0a..65084496 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -4903,6 +4903,30 @@
         Removed
       </message>
 
+      <message name="IDS_CABLEV2_QR_TITLE" desc="The title on a screen that confirms that the user wants to connect to a computer that displayed a QR code.">
+        Connect with QR Code?
+      </message>
+
+      <message name="IDS_CABLEV2_QR_BODY_PHONE" desc="The body text on a screen that confirms that the user wants to connect to a computer that displayed a QR code. This message is specific to phones and won't appear on tablets.">
+        You can use this phone to sign in on the computer that’s displaying this QR code.
+      </message>
+
+      <message name="IDS_CABLEV2_QR_BODY_TABLET" desc="The body text on a screen that confirms that the user wants to connect to a computer that displayed a QR code. This message is specific to tablets and won't appear on phones.">
+        You can use this tablet to sign in on the computer that’s displaying this QR code.
+      </message>
+
+      <message name="IDS_CABLEV2_QR_REMEMBER" desc="The label of a checkbox that appears on a screen confirming that the user wants to connect to a computer from which they just scanned a QR code. If the computer is 'remembered' then it'll be able to connect to the phone automatically in the future. The 'computer' is a laptop, desktop, or (eventually) another phone or tablet.">
+        Remember this computer
+      </message>
+
+      <message name="IDS_CABLEV2_QR_ALLOW" desc="The label of a button that the user presses to confirm that they want to connect to a computer from which they just scanned a QR code.">
+        Allow
+      </message>
+
+      <message name="IDS_CABLEV2_QR_DONT_ALLOW" desc="The label of a button that the user can press to decline to connect to a computer from which they just scanned a QR code.">
+        Don’t allow
+      </message>
+
       <!-- QR Code -->
       <message name="IDS_QR_CODE_SHARE_ICON_LABEL" desc="Icon label for sharing with QR Code activity.">
         QR Code
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_ALLOW.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_ALLOW.png.sha1
new file mode 100644
index 0000000..a9dc946
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_ALLOW.png.sha1
@@ -0,0 +1 @@
+866c1df3ee25513a4f54563a7f78d34875d02e72
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_BODY_PHONE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_BODY_PHONE.png.sha1
new file mode 100644
index 0000000..a9dc946
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_BODY_PHONE.png.sha1
@@ -0,0 +1 @@
+866c1df3ee25513a4f54563a7f78d34875d02e72
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_BODY_TABLET.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_BODY_TABLET.png.sha1
new file mode 100644
index 0000000..a9dc946
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_BODY_TABLET.png.sha1
@@ -0,0 +1 @@
+866c1df3ee25513a4f54563a7f78d34875d02e72
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_DONT_ALLOW.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_DONT_ALLOW.png.sha1
new file mode 100644
index 0000000..a9dc946
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_DONT_ALLOW.png.sha1
@@ -0,0 +1 @@
+866c1df3ee25513a4f54563a7f78d34875d02e72
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_REMEMBER.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_REMEMBER.png.sha1
new file mode 100644
index 0000000..a9dc946
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_REMEMBER.png.sha1
@@ -0,0 +1 @@
+866c1df3ee25513a4f54563a7f78d34875d02e72
\ No newline at end of file
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_TITLE.png.sha1 b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_TITLE.png.sha1
new file mode 100644
index 0000000..a9dc946
--- /dev/null
+++ b/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_CABLEV2_QR_TITLE.png.sha1
@@ -0,0 +1 @@
+866c1df3ee25513a4f54563a7f78d34875d02e72
\ No newline at end of file
diff --git a/chrome/browser/ui/app_list/app_list_client_impl.cc b/chrome/browser/ui/app_list/app_list_client_impl.cc
index cbad68e..bad11aa 100644
--- a/chrome/browser/ui/app_list/app_list_client_impl.cc
+++ b/chrome/browser/ui/app_list/app_list_client_impl.cc
@@ -9,6 +9,7 @@
 #include <utility>
 #include <vector>
 
+#include "ash/constants/ash_features.h"
 #include "ash/public/cpp/app_list/app_list_controller.h"
 #include "ash/public/cpp/new_window_delegate.h"
 #include "ash/public/cpp/shelf_model.h"
@@ -20,6 +21,7 @@
 #include "base/strings/strcat.h"
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "chrome/browser/ash/crosapi/browser_util.h"
+#include "chrome/browser/ash/crosapi/url_handler_ash.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/profiles/profile_manager.h"
 #include "chrome/browser/search_engines/template_url_service_factory.h"
@@ -45,8 +47,11 @@
 #include "chrome/browser/ui/browser_navigator.h"
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/browser_window.h"
+#include "chrome/browser/ui/webui/chrome_web_ui_controller_factory.h"
+#include "chromeos/crosapi/cpp/gurl_os_handler_utils.h"
 #include "components/session_manager/core/session_manager.h"
 #include "extensions/common/extension.h"
+#include "ui/base/page_transition_types.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/display/types/display_constants.h"
@@ -125,17 +130,32 @@
     current_model_updater_->SetActive(false);
 }
 
-void AppListClientImpl::StartZeroStateSearch(base::OnceClosure on_done,
-                                             base::TimeDelta timeout) {
-  // TODO(https://crbug.com/1269115): Refresh the zero state results.
-  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
-      FROM_HERE, std::move(on_done), timeout);
+void AppListClientImpl::StartSearch(const std::u16string& trimmed_query) {
+  // TODO(crbug.com/1269115): In the productivity launcher we handle empty
+  // queries, eg. from a user deleting a query, by re-routing them to
+  // StartZeroStateSearch. We may want to change this behavior so that ash calls
+  // StartZeroStateSearch directly.
+  if (search_controller_) {
+    if (trimmed_query.empty() &&
+        ash::features::IsProductivityLauncherEnabled()) {
+      // We use a long timeout here because the we don't have an
+      // animation-related deadline for these results, unlike a call to
+      // StartZeroStateSearch.
+      StartZeroStateSearch(base::DoNothing(), base::Seconds(1));
+    } else {
+      search_controller_->StartSearch(trimmed_query);
+    }
+    OnSearchStarted();
+  }
 }
 
-void AppListClientImpl::StartSearch(const std::u16string& trimmed_query) {
+void AppListClientImpl::StartZeroStateSearch(base::OnceClosure on_done,
+                                             base::TimeDelta timeout) {
   if (search_controller_) {
-    search_controller_->Start(trimmed_query);
+    search_controller_->StartZeroState(std::move(on_done), timeout);
     OnSearchStarted();
+  } else {
+    std::move(on_done).Run();
   }
 }
 
@@ -301,8 +321,11 @@
 
 void AppListClientImpl::OnAppListVisibilityWillChange(bool visible) {
   app_list_target_visibility_ = visible;
-  if (visible && search_controller_)
-    search_controller_->Start(std::u16string());
+  // TODO(crbug.com/1258415): This is only used in the old launcher, and can be
+  // removed once the productivity launcher is launched.
+  if (visible && search_controller_ &&
+      !ash::features::IsProductivityLauncherEnabled())
+    search_controller_->StartSearch(std::u16string());
 }
 
 void AppListClientImpl::OnAppListVisibilityChanged(bool visible) {
@@ -531,7 +554,18 @@
                                 ui::PageTransition transition,
                                 WindowOpenDisposition disposition) {
   if (crosapi::browser_util::IsLacrosPrimaryBrowser()) {
-    ash::NewWindowDelegate::GetPrimary()->OpenUrl(url, true);
+    const GURL sanitized_url =
+        crosapi::gurl_os_handler_utils::SanitizeAshURL(url);
+    if ((PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_TYPED) ||
+         PageTransitionCoreTypeIs(transition, ui::PAGE_TRANSITION_GENERATED)) &&
+        ChromeWebUIControllerFactory::GetInstance()->CanHandleUrl(
+            sanitized_url)) {
+      // Let our os url handler take care of the call.
+      crosapi::UrlHandlerAsh().OpenUrl(sanitized_url);
+    } else {
+      // Send the url to the current primary browser.
+      ash::NewWindowDelegate::GetPrimary()->OpenUrl(url, true);
+    }
   } else {
     NavigateParams params(profile, url, transition);
     params.disposition = disposition;
diff --git a/chrome/browser/ui/app_list/search/search_controller.h b/chrome/browser/ui/app_list/search/search_controller.h
index 9204da3..b39ae217 100644
--- a/chrome/browser/ui/app_list/search/search_controller.h
+++ b/chrome/browser/ui/app_list/search/search_controller.h
@@ -69,7 +69,9 @@
 
   virtual void InitializeRankers() {}
 
-  virtual void Start(const std::u16string& query) = 0;
+  virtual void StartSearch(const std::u16string& query) = 0;
+  virtual void StartZeroState(base::OnceClosure on_done,
+                              base::TimeDelta timeout) = 0;
   // TODO(crbug.com/1199206): We should rename this to AppListClosing for
   // consistency with AppListShown.
   virtual void ViewClosing() = 0;
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl.cc b/chrome/browser/ui/app_list/search/search_controller_impl.cc
index d3ecff4..666da76 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.cc
@@ -56,7 +56,7 @@
   mixer_->InitializeRankers(profile_);
 }
 
-void SearchControllerImpl::Start(const std::u16string& query) {
+void SearchControllerImpl::StartSearch(const std::u16string& query) {
   session_start_ = base::Time::Now();
   dispatching_query_ = true;
   ash::RecordLauncherIssuedSearchQueryLength(query.length());
@@ -74,6 +74,14 @@
   OnResultsChanged();
 }
 
+void SearchControllerImpl::StartZeroState(base::OnceClosure on_done,
+                                          base::TimeDelta timeout) {
+  // Only used for the productivity launcher.
+  // TODO(crbug.com/1269115): Unimplemented.
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, std::move(on_done), timeout);
+}
+
 void SearchControllerImpl::ViewClosing() {
   for (const auto& provider : providers_)
     provider->ViewClosing();
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl.h b/chrome/browser/ui/app_list/search/search_controller_impl.h
index d472d91..e6968b2 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl.h
+++ b/chrome/browser/ui/app_list/search/search_controller_impl.h
@@ -53,7 +53,9 @@
 
   // SearchController:
   void InitializeRankers() override;
-  void Start(const std::u16string& query) override;
+  void StartSearch(const std::u16string& query) override;
+  void StartZeroState(base::OnceClosure on_done,
+                      base::TimeDelta timeout) override;
   void ViewClosing() override;
   void OpenResult(ChromeSearchResult* result, int event_flags) override;
   void InvokeResultAction(ChromeSearchResult* result,
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
index 4cf6f22..4957c457 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.cc
@@ -50,7 +50,7 @@
 
 SearchControllerImplNew::~SearchControllerImplNew() {}
 
-void SearchControllerImplNew::Start(const std::u16string& query) {
+void SearchControllerImplNew::StartSearch(const std::u16string& query) {
   session_start_ = base::Time::Now();
 
   // TODO(crbug.com/1199206): We should move this histogram logic somewhere
@@ -68,6 +68,12 @@
     provider->Start(query);
 }
 
+void SearchControllerImplNew::StartZeroState(base::OnceClosure on_done,
+                                             base::TimeDelta timeout) {
+  base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
+      FROM_HERE, std::move(on_done), timeout);
+}
+
 void SearchControllerImplNew::OpenResult(ChromeSearchResult* result,
                                          int event_flags) {
   // This can happen in certain circumstances due to races. See
diff --git a/chrome/browser/ui/app_list/search/search_controller_impl_new.h b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
index 59b1816..7f6bb96c 100644
--- a/chrome/browser/ui/app_list/search/search_controller_impl_new.h
+++ b/chrome/browser/ui/app_list/search/search_controller_impl_new.h
@@ -53,7 +53,9 @@
   SearchControllerImplNew& operator=(const SearchControllerImplNew&) = delete;
 
   // SearchController:
-  void Start(const std::u16string& query) override;
+  void StartSearch(const std::u16string& query) override;
+  void StartZeroState(base::OnceClosure on_done,
+                      base::TimeDelta timeout) override;
   void OpenResult(ChromeSearchResult* result, int event_flags) override;
   void InvokeResultAction(ChromeSearchResult* result,
                           ash::SearchResultActionType action) override;
diff --git a/chrome/browser/ui/ash/capture_mode/capture_mode_browsertest.cc b/chrome/browser/ui/ash/capture_mode/capture_mode_browsertest.cc
index 1240131..a60fecd 100644
--- a/chrome/browser/ui/ash/capture_mode/capture_mode_browsertest.cc
+++ b/chrome/browser/ui/ash/capture_mode/capture_mode_browsertest.cc
@@ -146,8 +146,17 @@
   EXPECT_TRUE(shell_test_api.IsContextMenuShown());
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnVideoEndDismissed \
+  DISABLED_DlpWarningDialogOnVideoEndDismissed
+#else
+#define MAYBE_DlpWarningDialogOnVideoEndDismissed \
+  DlpWarningDialogOnVideoEndDismissed
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnVideoEndDismissed) {
+                       MAYBE_DlpWarningDialogOnVideoEndDismissed) {
   ASSERT_TRUE(browser());
   StartVideoRecording();
 
@@ -174,8 +183,17 @@
   loop.Run();
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnVideoEndAccepted \
+  DISABLED_DlpWarningDialogOnVideoEndAccepted
+#else
+#define MAYBE_DlpWarningDialogOnVideoEndAccepted \
+  DlpWarningDialogOnVideoEndAccepted
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnVideoEndAccepted) {
+                       MAYBE_DlpWarningDialogOnVideoEndAccepted) {
   ASSERT_TRUE(browser());
   StartVideoRecording();
 
@@ -196,8 +214,17 @@
   loop.Run();
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnSessionInitDismissed \
+  DISABLED_DlpWarningDialogOnSessionInitDismissed
+#else
+#define MAYBE_DlpWarningDialogOnSessionInitDismissed \
+  DlpWarningDialogOnSessionInitDismissed
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnSessionInitDismissed) {
+                       MAYBE_DlpWarningDialogOnSessionInitDismissed) {
   ASSERT_TRUE(browser());
 
   MarkActiveTabAsDlpWarnedForScreenCapture(browser());
@@ -217,8 +244,17 @@
   EXPECT_FALSE(test_api.IsPendingDlpCheck());
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnSessionInitAccepted \
+  DISABLED_DlpWarningDialogOnSessionInitAccepted
+#else
+#define MAYBE_DlpWarningDialogOnSessionInitAccepted \
+  DlpWarningDialogOnSessionInitAccepted
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnSessionInitAccepted) {
+                       MAYBE_DlpWarningDialogOnSessionInitAccepted) {
   ASSERT_TRUE(browser());
 
   MarkActiveTabAsDlpWarnedForScreenCapture(browser());
@@ -238,8 +274,17 @@
   EXPECT_FALSE(test_api.IsPendingDlpCheck());
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnPerformingCaptureDismissed \
+  DISABLED_DlpWarningDialogOnPerformingCaptureDismissed
+#else
+#define MAYBE_DlpWarningDialogOnPerformingCaptureDismissed \
+  DlpWarningDialogOnPerformingCaptureDismissed
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnPerformingCaptureDismissed) {
+                       MAYBE_DlpWarningDialogOnPerformingCaptureDismissed) {
   ASSERT_TRUE(browser());
 
   // Start the session before a window becomes restricted.
@@ -266,8 +311,17 @@
   EXPECT_FALSE(test_api.IsPendingDlpCheck());
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnPerformingCaptureAccepted \
+  DISABLED_DlpWarningDialogOnPerformingCaptureAccepted
+#else
+#define MAYBE_DlpWarningDialogOnPerformingCaptureAccepted \
+  DlpWarningDialogOnPerformingCaptureAccepted
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnPerformingCaptureAccepted) {
+                       MAYBE_DlpWarningDialogOnPerformingCaptureAccepted) {
   ASSERT_TRUE(browser());
 
   // Start the session before a window becomes restricted.
@@ -297,8 +351,17 @@
   loop.Run();
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnCountdownEndDismissed \
+  DISABLED_DlpWarningDialogOnCountdownEndDismissed
+#else
+#define MAYBE_DlpWarningDialogOnCountdownEndDismissed \
+  DlpWarningDialogOnCountdownEndDismissed
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnCountdownEndDismissed) {
+                       MAYBE_DlpWarningDialogOnCountdownEndDismissed) {
   ASSERT_TRUE(browser());
   ash::CaptureModeTestApi test_api;
   test_api.StartForFullscreen(/*for_video=*/true);
@@ -326,8 +389,17 @@
   EXPECT_FALSE(test_api.IsPendingDlpCheck());
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnCountdownEndAccepted \
+  DISABLED_DlpWarningDialogOnCountdownEndAccepted
+#else
+#define MAYBE_DlpWarningDialogOnCountdownEndAccepted \
+  DlpWarningDialogOnCountdownEndAccepted
+#endif
 IN_PROC_BROWSER_TEST_F(CaptureModeBrowserTest,
-                       DlpWarningDialogOnCountdownEndAccepted) {
+                       MAYBE_DlpWarningDialogOnCountdownEndAccepted) {
   ASSERT_TRUE(browser());
   ash::CaptureModeTestApi test_api;
   test_api.StartForFullscreen(/*for_video=*/true);
@@ -361,9 +433,18 @@
   loop.Run();
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnCaptureScreenshotsOfAllDisplaysDismissed \
+  DISABLED_DlpWarningDialogOnCaptureScreenshotsOfAllDisplaysDismissed
+#else
+#define MAYBE_DlpWarningDialogOnCaptureScreenshotsOfAllDisplaysDismissed \
+  DlpWarningDialogOnCaptureScreenshotsOfAllDisplaysDismissed
+#endif
 IN_PROC_BROWSER_TEST_F(
     CaptureModeBrowserTest,
-    DlpWarningDialogOnCaptureScreenshotsOfAllDisplaysDismissed) {
+    MAYBE_DlpWarningDialogOnCaptureScreenshotsOfAllDisplaysDismissed) {
   ASSERT_TRUE(browser());
 
   MarkActiveTabAsDlpWarnedForScreenCapture(browser());
@@ -382,9 +463,18 @@
   EXPECT_FALSE(test_api.IsPendingDlpCheck());
 }
 
+// TODO(crbug.com/1281985) Disabled due to MSAN use-of-uninitialized-value
+// errors.
+#if defined(OS_CHROMEOS) && defined(MEMORY_SANITIZER)
+#define MAYBE_DlpWarningDialogOnFullscreenScreenCaptureShortcutAccepted \
+  DISABLED_DlpWarningDialogOnFullscreenScreenCaptureShortcutAccepted
+#else
+#define MAYBE_DlpWarningDialogOnFullscreenScreenCaptureShortcutAccepted \
+  DlpWarningDialogOnFullscreenScreenCaptureShortcutAccepted
+#endif
 IN_PROC_BROWSER_TEST_F(
     CaptureModeBrowserTest,
-    DlpWarningDialogOnFullscreenScreenCaptureShortcutAccepted) {
+    MAYBE_DlpWarningDialogOnFullscreenScreenCaptureShortcutAccepted) {
   ASSERT_TRUE(browser());
 
   MarkActiveTabAsDlpWarnedForScreenCapture(browser());
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index 5b32662b..1e15458 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -315,10 +315,3 @@
 void ChromeShellDelegate::ResetDisableLoggingRedirectForTesting() {
   disable_logging_redirect_for_testing.reset();
 }
-
-const GURL& ChromeShellDelegate::GetLastCommittedURLForWindowIfAny(
-    aura::Window* window) {
-  content::WebContents* contents =
-      GetActiveWebContentsForNativeBrowserWindow(window);
-  return contents ? contents->GetLastCommittedURL() : GURL::EmptyGURL();
-}
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.h b/chrome/browser/ui/ash/chrome_shell_delegate.h
index 07155ed..02b7e5dca 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.h
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.h
@@ -9,7 +9,6 @@
 
 #include "ash/shell_delegate.h"
 #include "base/callback_forward.h"
-#include "url/gurl.h"
 
 class ChromeShellDelegate : public ash::ShellDelegate {
  public:
@@ -60,7 +59,6 @@
   void OpenFeedbackPageForPersistentDesksBar() override;
   static void SetDisableLoggingRedirectForTesting(bool value);
   static void ResetDisableLoggingRedirectForTesting();
-  const GURL& GetLastCommittedURLForWindowIfAny(aura::Window* window) override;
 };
 
 #endif  // CHROME_BROWSER_UI_ASH_CHROME_SHELL_DELEGATE_H_
diff --git a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
index 50066df..bbd335a 100644
--- a/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
+++ b/chrome/browser/ui/ash/desks_templates/desks_templates_client_browsertest.cc
@@ -66,6 +66,7 @@
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/aura/client/focus_client.h"
+#include "ui/compositor/layer.h"
 #include "ui/display/screen.h"
 #include "ui/events/test/event_generator.h"
 #include "ui/views/controls/button/button.h"
@@ -1132,6 +1133,79 @@
             settings_window->parent());
 }
 
+// Tests that launching a template that contains a system web app will move the
+// existing instance of the system web app to the current desk.
+IN_PROC_BROWSER_TEST_F(DesksTemplatesClientTest,
+                       NativeUILaunchTemplateWithSWAExisting) {
+  Profile* profile = browser()->profile();
+
+  // Create the settings app, which is a system web app.
+  CreateSettingsSystemWebApp(profile);
+
+  aura::Window* settings_window = FindBrowserWindow(kSettingsWindowId);
+  ASSERT_TRUE(settings_window);
+  EXPECT_EQ(2u, BrowserList::GetInstance()->size());
+
+  // Give the settings app a known position.
+  const gfx::Rect settings_bounds(100, 100, 600, 400);
+  settings_window->SetBounds(settings_bounds);
+  // Focus the browser so that the settings window is stacked at the bottom.
+  browser()->window()->GetNativeWindow()->Focus();
+  ASSERT_THAT(settings_window->parent()->children(),
+              ElementsAre(settings_window, _));
+
+  // Enter overview and save the current desk as a template.
+  ash::ToggleOverview();
+  ash::WaitForOverviewEnterAnimation();
+  views::Button* save_desk_as_template_button =
+      ash::GetSaveDeskAsTemplateButton();
+  ASSERT_TRUE(save_desk_as_template_button);
+  ClickButton(save_desk_as_template_button);
+
+  // Exit overview and move the settings window to a new place and stack it on
+  // top so that we can later verify that it has been placed and stacked
+  // correctly.
+  ash::ToggleOverview();
+  ash::WaitForOverviewExitAnimation();
+  settings_window->SetBounds(gfx::Rect(150, 150, 650, 500));
+  settings_window->Focus();
+
+  // Enter overview, head over to the desks templates grid and launch the
+  // template.
+  ash::ToggleOverview();
+  ash::WaitForOverviewEnterAnimation();
+  views::Button* zero_state_templates_button =
+      ash::GetZeroStateDesksTemplatesButton();
+  ASSERT_TRUE(zero_state_templates_button);
+  ClickButton(zero_state_templates_button);
+
+  ash::WaitForDesksTemplatesUI();
+  views::Button* template_item = ash::GetTemplateItemButton(/*index=*/0);
+  ASSERT_TRUE(template_item);
+  ClickButton(template_item);
+
+  // Wait for the tabs to load.
+  content::RunAllTasksUntilIdle();
+
+  // Exit overview.
+  ash::ToggleOverview();
+  ash::WaitForOverviewExitAnimation();
+
+  ash::DesksController* desks_controller = ash::DesksController::Get();
+  ASSERT_EQ(1, desks_controller->GetActiveDeskIndex());
+
+  // We launch a new browser window, but not a new settings app. Verify that the
+  // window has been moved to the right place and stacked at the bottom.
+  EXPECT_EQ(3u, BrowserList::GetInstance()->size());
+  EXPECT_TRUE(desks_controller->BelongsToActiveDesk(settings_window));
+  EXPECT_EQ(settings_bounds, settings_window->bounds());
+
+  // TODO(crbug.com/1281393): Verify that the element order is correct.
+
+  // Tests that there is no clipping on the settings window.
+  EXPECT_EQ(gfx::Rect(), settings_window->layer()->clip_rect());
+}
+
 class DesksTemplatesClientArcTest : public InProcessBrowserTest {
  public:
   DesksTemplatesClientArcTest() {
diff --git a/chrome/browser/ui/ash/session_controller_client_impl.cc b/chrome/browser/ui/ash/session_controller_client_impl.cc
index 2747ada..d42b3c61 100644
--- a/chrome/browser/ui/ash/session_controller_client_impl.cc
+++ b/chrome/browser/ui/ash/session_controller_client_impl.cc
@@ -203,6 +203,10 @@
   return g_session_controller_client_instance;
 }
 
+void SessionControllerClientImpl::PrepareForLock(base::OnceClosure callback) {
+  session_controller_->PrepareForLock(std::move(callback));
+}
+
 void SessionControllerClientImpl::StartLock(StartLockCallback callback) {
   session_controller_->StartLock(std::move(callback));
 }
diff --git a/chrome/browser/ui/ash/session_controller_client_impl.h b/chrome/browser/ui/ash/session_controller_client_impl.h
index 9b32a854..f80739a7 100644
--- a/chrome/browser/ui/ash/session_controller_client_impl.h
+++ b/chrome/browser/ui/ash/session_controller_client_impl.h
@@ -58,6 +58,9 @@
 
   static SessionControllerClientImpl* Get();
 
+  // Calls SessionController to prepare locking ash.
+  void PrepareForLock(base::OnceClosure callback);
+
   // Calls SessionController to start locking ash. |callback| will be invoked
   // to indicate whether the lock is successful. If |locked| is true, the post
   // lock animation is finished and ash is fully locked. Otherwise, the lock
diff --git a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
index 6ad8d723..bb5282c 100644
--- a/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
+++ b/chrome/browser/ui/ash/shelf/app_service/exo_app_type_resolver.cc
@@ -13,6 +13,7 @@
 #include "components/app_restore/app_restore_utils.h"
 #include "components/app_restore/window_properties.h"
 #include "components/exo/permission.h"
+#include "components/exo/shell_surface_util.h"
 #include "ui/aura/client/aura_constants.h"
 #include "ui/base/class_property.h"
 
@@ -48,6 +49,14 @@
     // Auto-maximize causes compatibility issues, and we don't need it anyway.
     out_properties_container.SetProperty(chromeos::kAutoMaximizeXdgShellEnabled,
                                          false);
+
+    // In some instances we don't want new borealis windows to steal focus,
+    // instead they are created as minimized windows.
+    // TODO(b/210569001): this is intended to be a temporary solution.
+    if (borealis::BorealisWindowManager::ShouldNewWindowBeMinimized()) {
+      out_properties_container.SetProperty(aura::client::kShowStateKey,
+                                           ui::SHOW_STATE_MINIMIZED);
+    }
   }
 
   auto task_id = arc::GetTaskIdFromWindowAppId(params.app_id);
diff --git a/chrome/browser/ui/ash/test_session_controller.cc b/chrome/browser/ui/ash/test_session_controller.cc
index a3ac53d..c833212 100644
--- a/chrome/browser/ui/ash/test_session_controller.cc
+++ b/chrome/browser/ui/ash/test_session_controller.cc
@@ -34,6 +34,10 @@
   ++set_user_session_order_count_;
 }
 
+void TestSessionController::PrepareForLock(PrepareForLockCallback callback) {
+  std::move(callback).Run();
+}
+
 void TestSessionController::StartLock(StartLockCallback callback) {
   std::move(callback).Run(true);
 }
diff --git a/chrome/browser/ui/ash/test_session_controller.h b/chrome/browser/ui/ash/test_session_controller.h
index 76a28e54..beefd1c90 100644
--- a/chrome/browser/ui/ash/test_session_controller.h
+++ b/chrome/browser/ui/ash/test_session_controller.h
@@ -56,6 +56,7 @@
   void UpdateUserSession(const ash::UserSession& user_session) override;
   void SetUserSessionOrder(
       const std::vector<uint32_t>& user_session_order) override;
+  void PrepareForLock(PrepareForLockCallback callback) override;
   void StartLock(StartLockCallback callback) override;
   void NotifyChromeLockAnimationsComplete() override;
   void RunUnlockAnimation(RunUnlockAnimationCallback callback) override;
diff --git a/chrome/browser/ui/views/crostini/crostini_recovery_view_browsertest.cc b/chrome/browser/ui/views/crostini/crostini_recovery_view_browsertest.cc
index ab1df2a..fa8140d1 100644
--- a/chrome/browser/ui/views/crostini/crostini_recovery_view_browsertest.cc
+++ b/chrome/browser/ui/views/crostini/crostini_recovery_view_browsertest.cc
@@ -133,7 +133,8 @@
       static_cast<base::HistogramBase::Sample>(kUiSurface), 0);
 }
 
-IN_PROC_BROWSER_TEST_F(CrostiniRecoveryViewBrowserTest, Cancel) {
+// TODO(https://crbug.com/1105487): This test is flaky.
+IN_PROC_BROWSER_TEST_F(CrostiniRecoveryViewBrowserTest, DISABLED_Cancel) {
   base::HistogramTester histogram_tester;
 
   SetUncleanStartup();
diff --git a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
index ed63c39..8627819 100644
--- a/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
+++ b/chrome/browser/ui/web_applications/web_share_target_browsertest.cc
@@ -380,6 +380,7 @@
 
   content::WebContents* web_contents = ShareToTarget("share_single_file()");
   EXPECT_EQ(std::string(12, '*'), ReadTextContent(web_contents, "image"));
+  EXPECT_EQ("sample.webp", ReadTextContent(web_contents, "image_filename"));
 }
 
 IN_PROC_BROWSER_TEST_F(WebShareTargetBrowserTest, ShareMultimedia) {
@@ -393,6 +394,8 @@
   content::WebContents* web_contents = ShareToTarget("share_multiple_files()");
   EXPECT_EQ(std::string(345, '*'), ReadTextContent(web_contents, "audio"));
   EXPECT_EQ(std::string(67890, '*'), ReadTextContent(web_contents, "video"));
+  EXPECT_EQ("sam.ple.mp3", ReadTextContent(web_contents, "audio_filename"));
+  EXPECT_EQ("_sample.mp4", ReadTextContent(web_contents, "video_filename"));
 }
 
 IN_PROC_BROWSER_TEST_F(WebShareTargetBrowserTest, ShareToPartialWild) {
diff --git a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
index 5801a25e..b6da902 100644
--- a/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
+++ b/chrome/browser/ui/webui/app_management/app_management_page_handler.cc
@@ -210,7 +210,6 @@
 void AppManagementPageHandler::SetPermission(
     const std::string& app_id,
     apps::mojom::PermissionPtr permission) {
-  DLOG(ERROR) << "set permission";
   apps::AppServiceProxyFactory::GetForProfile(profile_)->SetPermission(
       app_id, std::move(permission));
 }
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
index 76dc06e..5b417c3 100644
--- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
+++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -1399,16 +1399,18 @@
       GURL(chrome::kChromeUIAddSupervisionURL),
       GURL(chrome::kOsUIAddSupervisionURL),
       GURL(chrome::kChromeUIAppDisabledURL), GURL(chrome::kOsUIAppDisabledURL),
-      GURL(chrome::kOsUIArcGraphicsTracingURL),
-      GURL(chrome::kOsUIArcOverviewTracingURL),
-      GURL(chrome::kOsUIArcPowerControlURL),
-      GURL(chrome::kOsUIAssistantOptInURL),
-      GURL(chrome::kOsUIBluetoothPairingURL), GURL(chrome::kOsUIComponentsUrl),
-      GURL(chrome::kChromeUICrashesUrl), GURL(chrome::kOsUICrashesUrl),
-      GURL(chrome::kOsUICreditsURL), GURL(chrome::kOsUICrostiniCreditsURL),
-      GURL(chrome::kOsUICrostiniInstallerUrl),
-      GURL(chrome::kOsUICrostiniUpgraderUrl), GURL(chrome::kOsUICryptohomeURL),
-      GURL(chrome::kOsUIDeviceLogUrl), GURL(chrome::kOsUIDiagnosticsAppURL),
+      GURL(chrome::kChromeUIArcGraphicsTracingURL),
+      GURL(chrome::kChromeUIArcOverviewTracingURL),
+      GURL(chrome::kChromeUIArcPowerControlURL),
+      GURL(chrome::kChromeUIAssistantOptInURL),
+      GURL(chrome::kChromeUIBluetoothPairingURL),
+      GURL(chrome::kOsUIComponentsUrl), GURL(chrome::kChromeUICrashesUrl),
+      GURL(chrome::kOsUICrashesUrl), GURL(chrome::kOsUICreditsURL),
+      GURL(chrome::kChromeUICrostiniCreditsURL),
+      GURL(chrome::kChromeUICrostiniInstallerUrl),
+      GURL(chrome::kChromeUICrostiniUpgraderUrl),
+      GURL(chrome::kChromeUICryptohomeURL), GURL(chrome::kOsUIDeviceLogUrl),
+      GURL(chrome::kChromeUIDiagnosticsAppURL),
       GURL(chrome::kChromeUIDriveInternalsUrl),
       GURL(chrome::kOsUIDriveInternalsUrl),
       GURL(chrome::kChromeUIEmojiPickerURL), GURL(chrome::kOsUIEmojiPickerURL),
@@ -1416,25 +1418,26 @@
       GURL(chrome::kOsUIFileManagerURL), GURL(chrome::kChromeUIFlagsURL),
       GURL(chrome::kOsUIFlagsURL), GURL(chrome::kOsUIGpuURL),
       GURL(chrome::kOsUIHistogramsURL),
-      GURL(chrome::kOsUIIntenetConfigDialogURL),
-      GURL(chrome::kOsUIIntenetDetailDialogURL),
+      GURL(chrome::kChromeUIIntenetConfigDialogURL),
+      GURL(chrome::kChromeUIIntenetDetailDialogURL),
       GURL(chrome::kOsUIInvalidationsUrl),
       GURL(chrome::kChromeUILockScreenNetworkURL),
-      GURL(chrome::kOsUILockScreenStartReauthURL),
-      GURL(chrome::kChromeUILockScreenNetworkURL),
-      GURL(chrome::kOsUILockScreenStartReauthURL),
-      GURL(chrome::kOsUIMobileSetupURL), GURL(chrome::kOsUIMultiDeviceSetupUrl),
+      GURL(chrome::kChromeUILockScreenStartReauthURL),
+      GURL(chrome::kChromeUIMobileSetupURL),
+      GURL(chrome::kChromeUIMultiDeviceSetupUrl),
       GURL(chrome::kChromeUINetworkUrl), GURL(chrome::kOsUINetworkUrl),
-      GURL(chrome::kOsUIOSCreditsURL), GURL(chrome::kOsUIOSSettingsURL),
-      GURL(chrome::kOsUIPowerUrl), GURL(chrome::kOsUIPrintManagementUrl),
-      GURL(chrome::kOsUIRestartURL), GURL(chrome::kChromeUIScanningAppURL),
-      GURL(chrome::kOsUIScanningAppURL), GURL(chrome::kOsUISetTimeURL),
-      GURL(chrome::kChromeUIOSSettingsURL), GURL(chrome::kChromeUISettingsURL),
-      GURL(chrome::kOsUISettingsURL), GURL(chrome::kOsUISignInInternalsUrl),
-      GURL(chrome::kOsUISlowURL), GURL(chrome::kOsUISmbCredentialsURL),
-      GURL(chrome::kOsUISmbShareURL), GURL(chrome::kOsUISyncInternalsUrl),
-      GURL(chrome::kOsUISysInternalsUrl), GURL(chrome::kOsUIUserImageURL),
-      GURL(chrome::kOsUIVersionURL), GURL(chrome::kOsUIVmUrl),
+      GURL(chrome::kChromeUIOSCreditsURL), GURL(chrome::kOsUIOSSettingsURL),
+      GURL(chrome::kChromeUIPowerUrl),
+      GURL(chrome::kChromeUIPrintManagementUrl), GURL(chrome::kOsUIRestartURL),
+      GURL(chrome::kChromeUIScanningAppURL), GURL(chrome::kOsUIScanningAppURL),
+      GURL(chrome::kChromeUISetTimeURL), GURL(chrome::kChromeUIOSSettingsURL),
+      GURL(chrome::kChromeUISettingsURL), GURL(chrome::kOsUISettingsURL),
+      GURL(chrome::kOsUISignInInternalsUrl), GURL(chrome::kChromeUISlowURL),
+      GURL(chrome::kChromeUISmbCredentialsURL),
+      GURL(chrome::kChromeUISmbShareURL), GURL(chrome::kOsUISyncInternalsUrl),
+      GURL(chrome::kChromeUISysInternalsUrl),
+      GURL(chrome::kChromeUIUserImageURL), GURL(chrome::kOsUIVersionURL),
+      GURL(chrome::kChromeUIVmUrl),
       // The CL to land this didn't land yet. Once landed they need to be moved
       // to Lacros. However  - as the refactor might precede this, there is no
       // TODO for it.
diff --git a/chrome/browser/vr/elements/omnibox_formatting.cc b/chrome/browser/vr/elements/omnibox_formatting.cc
index 015c176..53baaa7 100644
--- a/chrome/browser/vr/elements/omnibox_formatting.cc
+++ b/chrome/browser/vr/elements/omnibox_formatting.cc
@@ -93,8 +93,8 @@
   const url::Component& scheme = parsed.scheme;
   const url::Component& host = parsed.host;
 
-  const std::u16string url_scheme =
-      formatted_url.substr(scheme.begin, scheme.len);
+  const base::StringPiece16 url_scheme =
+      base::StringPiece16(formatted_url).substr(scheme.begin, scheme.len);
 
   // Data URLs are rarely human-readable and can be used for spoofing, so draw
   // attention to the scheme to emphasize "this is just a bunch of data".  For
@@ -105,7 +105,7 @@
     ALL_BUT_HOST,
     NOTHING,
   } deemphasize_mode = NOTHING;
-  if (url_scheme == base::UTF8ToUTF16(url::kDataScheme))
+  if (url_scheme == url::kDataScheme16)
     deemphasize_mode = ALL_BUT_SCHEME;
   else if (host.is_nonempty())
     deemphasize_mode = ALL_BUT_HOST;
diff --git a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
index 09b529e4..fe5dafc 100644
--- a/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
+++ b/chrome/browser/web_applications/app_service/web_apps_publisher_host_browsertest.cc
@@ -815,6 +815,71 @@
 
   EXPECT_EQ("POST", ReadTextContent(web_contents, "method"));
   EXPECT_EQ(kData, ReadTextContent(web_contents, "image"));
+  EXPECT_EQ("sample.webp", ReadTextContent(web_contents, "image_filename"));
+}
+
+IN_PROC_BROWSER_TEST_F(WebAppsPublisherHostBrowserTest, ShareMultimedia) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  const AppId app_id = InstallWebAppFromManifest(
+      browser(),
+      embedded_test_server()->GetURL("/web_share_target/multimedia.html"));
+  const std::string kAudioContent(345, '*');
+  const std::string kVideoContent(67890, '*');
+
+  MockAppPublisher mock_app_publisher;
+  WebAppsPublisherHost web_apps_publisher_host(profile());
+  web_apps_publisher_host.SetPublisherForTesting(&mock_app_publisher);
+  web_apps_publisher_host.Init();
+  mock_app_publisher.Wait();
+  EXPECT_EQ(mock_app_publisher.get_deltas().size(), 1U);
+  EXPECT_FALSE(mock_app_publisher.get_deltas().back()->intent_filters.empty());
+
+  base::ScopedAllowBlockingForTesting allow_blocking;
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath audio_file =
+      temp_dir.GetPath().Append(FILE_PATH_LITERAL("sam.ple.mp3"));
+  ASSERT_TRUE(base::WriteFile(audio_file, kAudioContent));
+  base::FilePath video_file =
+      temp_dir.GetPath().Append(FILE_PATH_LITERAL("_sample.mp4"));
+  ASSERT_TRUE(base::WriteFile(video_file, kVideoContent));
+
+  ui_test_utils::AllBrowserTabAddedWaiter waiter;
+  {
+    crosapi::mojom::IntentPtr crosapi_intent = crosapi::mojom::Intent::New();
+    crosapi_intent->action = apps_util::kIntentActionSendMultiple;
+    crosapi_intent->mime_type = "*/*";
+    std::vector<crosapi::mojom::IntentFilePtr> crosapi_files;
+    {
+      auto crosapi_file = crosapi::mojom::IntentFile::New();
+      crosapi_file->file_path = audio_file;
+      crosapi_file->mime_type = "audio/mpeg";
+      crosapi_files.push_back(std::move(crosapi_file));
+    }
+    {
+      auto crosapi_file = crosapi::mojom::IntentFile::New();
+      crosapi_file->file_path = video_file;
+      crosapi_file->mime_type = "video/mp4";
+      crosapi_files.push_back(std::move(crosapi_file));
+    }
+    crosapi_intent->files = std::move(crosapi_files);
+
+    auto launch_params = apps::CreateCrosapiLaunchParamsWithEventFlags(
+        apps::AppServiceProxyFactory::GetForProfile(profile()), app_id,
+        /*event_flags=*/0, apps::mojom::LaunchSource::kFromSharesheet,
+        display::kInvalidDisplayId);
+    launch_params->intent = std::move(crosapi_intent);
+
+    static_cast<crosapi::mojom::AppController&>(web_apps_publisher_host)
+        .Launch(std::move(launch_params), base::DoNothing());
+  }
+  content::WebContents* const web_contents = waiter.Wait();
+  EXPECT_TRUE(content::WaitForLoadStop(web_contents));
+
+  EXPECT_EQ(kAudioContent, ReadTextContent(web_contents, "audio"));
+  EXPECT_EQ(kVideoContent, ReadTextContent(web_contents, "video"));
+  EXPECT_EQ("sam.ple.mp3", ReadTextContent(web_contents, "audio_filename"));
+  EXPECT_EQ("_sample.mp4", ReadTextContent(web_contents, "video_filename"));
 }
 
 }  // namespace web_app
diff --git a/chrome/build/mac.pgo.txt b/chrome/build/mac.pgo.txt
index 83ae248d..3c10d9b 100644
--- a/chrome/build/mac.pgo.txt
+++ b/chrome/build/mac.pgo.txt
@@ -1 +1 @@
-chrome-mac-main-1640086111-6df7aa4f6679fdde7b3eba30767ce2bc16209d02.profdata
+chrome-mac-main-1640109555-4f788d66531f70fd161dada09e32b5851b4fbaae.profdata
diff --git a/chrome/build/win32.pgo.txt b/chrome/build/win32.pgo.txt
index e08ed3d..1a01874 100644
--- a/chrome/build/win32.pgo.txt
+++ b/chrome/build/win32.pgo.txt
@@ -1 +1 @@
-chrome-win32-main-1640098480-b38354066ae7306f425172dd0747ef8f72129a43.profdata
+chrome-win32-main-1640120104-62f9b79b00a887290a876ff7e43bd61c1c1ee4e5.profdata
diff --git a/chrome/build/win64.pgo.txt b/chrome/build/win64.pgo.txt
index 6dba82e..d6bfcc6 100644
--- a/chrome/build/win64.pgo.txt
+++ b/chrome/build/win64.pgo.txt
@@ -1 +1 @@
-chrome-win64-main-1640076128-d59cda87adda071e89873e0874334903ea90dfa5.profdata
+chrome-win64-main-1640120104-5f892614cbc8389f8f2aaaa4f4b032ceaee1d505.profdata
diff --git a/chrome/common/chrome_features.cc b/chrome/common/chrome_features.cc
index 4c16548..ccfb8a0 100644
--- a/chrome/common/chrome_features.cc
+++ b/chrome/common/chrome_features.cc
@@ -52,6 +52,11 @@
 const base::Feature kAlwaysReinstallSystemWebApps{
     "ReinstallSystemWebApps", base::FEATURE_DISABLED_BY_DEFAULT};
 
+#if defined(OS_ANDROID)
+const base::Feature kAnonymousUpdateChecks{"AnonymousUpdateChecks",
+                                           base::FEATURE_ENABLED_BY_DEFAULT};
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 // Controls whether web apps can be installed via APKs on Chrome OS.
 const base::Feature kApkWebAppInstalls{"ApkWebAppInstalls",
diff --git a/chrome/common/chrome_features.h b/chrome/common/chrome_features.h
index ae39a1e2..6b14737 100644
--- a/chrome/common/chrome_features.h
+++ b/chrome/common/chrome_features.h
@@ -53,6 +53,11 @@
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kAlwaysReinstallSystemWebApps;
 
+#if defined(OS_ANDROID)
+COMPONENT_EXPORT(CHROME_FEATURES)
+extern const base::Feature kAnonymousUpdateChecks;
+#endif
+
 #if BUILDFLAG(IS_CHROMEOS_ASH)
 COMPONENT_EXPORT(CHROME_FEATURES)
 extern const base::Feature kApkWebAppInstalls;
diff --git a/chrome/common/webui_url_constants.cc b/chrome/common/webui_url_constants.cc
index 060c2db..eced5d8 100644
--- a/chrome/common/webui_url_constants.cc
+++ b/chrome/common/webui_url_constants.cc
@@ -357,47 +357,22 @@
 const char kOsUIAccountMigrationWelcomeURL[] = "os://account-migration-welcome";
 const char kOsUIAddSupervisionURL[] = "os://add-supervision";
 const char kOsUIAppDisabledURL[] = "os://app-disabled";
-const char kOsUIArcGraphicsTracingURL[] = "os://arc-graphics-tracing";
-const char kOsUIArcOverviewTracingURL[] = "os://arc-overview-tracing";
-const char kOsUIArcPowerControlURL[] = "os://arc-power-control";
-const char kOsUIAssistantOptInURL[] = "os://assistant-optin";
-const char kOsUIBluetoothPairingURL[] = "os://bluetooth-pairing";
 const char kOsUICrashesUrl[] = "os://crashes";
 const char kOsUICreditsURL[] = "os://credits";
-const char kOsUICrostiniCreditsURL[] = "os://crostini-credits";
-const char kOsUICrostiniInstallerUrl[] = "os://crostini-installer";
-const char kOsUICrostiniUpgraderUrl[] = "os://crostini-upgrader";
-const char kOsUICryptohomeURL[] = "os://cryptohome";
 const char kOsUIDeviceLogUrl[] = "os://device-log";
-const char kOsUIDiagnosticsAppURL[] = "os://diagnostics";
 const char kOsUIDriveInternalsUrl[] = "os://drive-internals";
 const char kOsUIEmojiPickerURL[] = "os://emoji-picker";
 const char kOsUIGpuURL[] = "os://gpu";
 const char kOsUIHistogramsURL[] = "os://histograms";
-const char kOsUIIntenetConfigDialogURL[] = "os://internet-config-dialog";
-const char kOsUIIntenetDetailDialogURL[] = "os://internet-detail-dialog";
 const char kOsUIInvalidationsUrl[] = "os://invalidations";
 const char kOsUILockScreenNetworkURL[] = "os://lock-network";
-const char kOsUILockScreenStartReauthURL[] = "os://lock-reauth";
-const char kOsUIMobileSetupURL[] = "os://mobilesetup";
-const char kOsUIMultiDeviceSetupUrl[] = "os://multidevice-setup";
 const char kOsUINetworkUrl[] = "os://network";
-const char kOsUIOSCreditsURL[] = "os://os-credits";
 const char kOsUIOSSettingsURL[] = "os://os-settings";
-const char kOsUIPowerUrl[] = "os://power";
-const char kOsUIPrintManagementUrl[] = "os://print-management";
 const char kOsUIRestartURL[] = "os://restart";
 const char kOsUIScanningAppURL[] = "os://scanning";
-const char kOsUISetTimeURL[] = "os://set-time";
 const char kOsUISettingsURL[] = "os://settings";
 const char kOsUISignInInternalsUrl[] = "os://signin-internals";
-const char kOsUISlowURL[] = "os://slow";
-const char kOsUISmbCredentialsURL[] = "os://smb-credentials-dialog";
-const char kOsUISmbShareURL[] = "os://smb-share-dialog";
 const char kOsUISyncInternalsUrl[] = "os://sync-internals";
-const char kOsUISysInternalsUrl[] = "os://sys-internals";
-const char kOsUIUserImageURL[] = "os://userimage";
-const char kOsUIVmUrl[] = "os://vm";
 
 // Keep alphabetized.
 
diff --git a/chrome/common/webui_url_constants.h b/chrome/common/webui_url_constants.h
index 8e8fa61..d3e07ab 100644
--- a/chrome/common/webui_url_constants.h
+++ b/chrome/common/webui_url_constants.h
@@ -340,47 +340,22 @@
 extern const char kOsUIAccountMigrationWelcomeURL[];
 extern const char kOsUIAddSupervisionURL[];
 extern const char kOsUIAppDisabledURL[];
-extern const char kOsUIArcGraphicsTracingURL[];
-extern const char kOsUIArcOverviewTracingURL[];
-extern const char kOsUIArcPowerControlURL[];
-extern const char kOsUIAssistantOptInURL[];
-extern const char kOsUIBluetoothPairingURL[];
 extern const char kOsUICrashesUrl[];
 extern const char kOsUICreditsURL[];
-extern const char kOsUICrostiniCreditsURL[];
-extern const char kOsUICrostiniInstallerUrl[];
-extern const char kOsUICrostiniUpgraderUrl[];
-extern const char kOsUICryptohomeURL[];
 extern const char kOsUIDeviceLogUrl[];
-extern const char kOsUIDiagnosticsAppURL[];
 extern const char kOsUIDriveInternalsUrl[];
 extern const char kOsUIEmojiPickerURL[];
 extern const char kOsUIGpuURL[];
 extern const char kOsUIHistogramsURL[];
-extern const char kOsUIIntenetConfigDialogURL[];
-extern const char kOsUIIntenetDetailDialogURL[];
 extern const char kOsUIInvalidationsUrl[];
 extern const char kOsUILockScreenNetworkURL[];
-extern const char kOsUILockScreenStartReauthURL[];
-extern const char kOsUIMobileSetupURL[];
-extern const char kOsUIMultiDeviceSetupUrl[];
 extern const char kOsUINetworkUrl[];
-extern const char kOsUIOSCreditsURL[];
 extern const char kOsUIOSSettingsURL[];
-extern const char kOsUIPowerUrl[];
-extern const char kOsUIPrintManagementUrl[];
 extern const char kOsUIRestartURL[];
 extern const char kOsUIScanningAppURL[];
-extern const char kOsUISetTimeURL[];
 extern const char kOsUISettingsURL[];
 extern const char kOsUISignInInternalsUrl[];
-extern const char kOsUISlowURL[];
-extern const char kOsUISmbCredentialsURL[];
-extern const char kOsUISmbShareURL[];
 extern const char kOsUISyncInternalsUrl[];
-extern const char kOsUISysInternalsUrl[];
-extern const char kOsUIUserImageURL[];
-extern const char kOsUIVmUrl[];
 
 // Returns true if this web UI is part of the "system UI". Generally this is
 // UI that opens in a window (not a browser tab) and that on other operating
diff --git a/chrome/test/data/web_share_target/service_worker.js b/chrome/test/data/web_share_target/service_worker.js
index 9be148c..d19631c7 100644
--- a/chrome/test/data/web_share_target/service_worker.js
+++ b/chrome/test/data/web_share_target/service_worker.js
@@ -42,12 +42,14 @@
     let field_index = 0;
     let files = undefined;
     let file_contents = '';
+    let file_names = '';
     let index = 0;
 
     function prepareField() {
       files = formData.getAll(
           file_fields[field_index]);  // sequence of File objects
       file_contents = '';
+      file_names = '';
       index = 0;
     }
 
@@ -57,6 +59,8 @@
       while (index === files.length) {
         body = body.replace(
             '{{' + file_fields[field_index] + '}}', file_contents);
+        body = body.replace(
+            '{{' + file_fields[field_index] + '_filename}}', file_names);
 
         ++field_index;
         if (field_index === file_fields.length) {
@@ -70,8 +74,10 @@
           await readAsFilePromise(fileReader, files[index], 'UTF-8');
       if (index > 0) {
         file_contents += ' ';
+        file_names += ' ';
       }
       file_contents += dataFromFileLoaded;
+      file_names += files[index].name;
       index += 1;
       return await progress();
     }
diff --git a/chrome/test/data/web_share_target/share.template.html b/chrome/test/data/web_share_target/share.template.html
index cc176d1..d009c061 100644
--- a/chrome/test/data/web_share_target/share.template.html
+++ b/chrome/test/data/web_share_target/share.template.html
@@ -15,9 +15,15 @@
   <section id="records">{{records}}</section>
   <section id="graphs">{{graphs}}</section>
   <section id="notes">{{notes}}</section>
-
   <section id="audio">{{audio}}</section>
   <section id="image">{{image}}</section>
   <section id="video">{{video}}</section>
+
+  <section id="records_filename">{{records_filename}}</section>
+  <section id="graphs_filename">{{graphs_filename}}</section>
+  <section id="notes_filename">{{notes_filename}}</section>
+  <section id="audio_filename">{{audio_filename}}</section>
+  <section id="image_filename">{{image_filename}}</section>
+  <section id="video_filename">{{video_filename}}</section>
 </body>
 </html>
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 17b54be..0dcaa1e2 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -621,6 +621,7 @@
     ":build_chai_grdp",
     ":build_web_ui_test_mojo_grdp",
     "bookmarks:build_grdp",
+    "cr_components:build_grdp",
     "cr_elements:build_grdp",
     "downloads:build_grdp",
     "read_later:build_grdp",
@@ -630,6 +631,7 @@
 
   grdp_files = [
     "$target_gen_dir/bookmarks/resources.grdp",
+    "$target_gen_dir/cr_components/resources.grdp",
     "$target_gen_dir/cr_elements/resources.grdp",
     "$target_gen_dir/chai_resources.grdp",
     "$target_gen_dir/downloads/resources.grdp",
diff --git a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
index 1ff490c..343fbd0 100644
--- a/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
+++ b/chrome/test/data/webui/chromeos/personalization_app/wallpaper_collections_element_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import {kMaximumLocalImagePreviews} from 'chrome://personalization/common/constants.js';
+import {kMaximumGooglePhotosPreviews, kMaximumLocalImagePreviews} from 'chrome://personalization/common/constants.js';
 import {emptyState} from 'chrome://personalization/trusted/personalization_state.js';
 import {promisifyIframeFunctionsForTesting, WallpaperCollections} from 'chrome://personalization/trusted/wallpaper/wallpaper_collections_element.js';
 
@@ -88,7 +88,9 @@
 
     wallpaperCollectionsElement = initElement(WallpaperCollections.is);
 
-    personalizationStore.data.wallpaper.googlePhotos.photos = [1, 2, 3, 4];
+    personalizationStore.data.wallpaper.googlePhotos.photos =
+        Array.from({length: kMaximumGooglePhotosPreviews + 1})
+            .map((_, i) => `foo://${i}`);
     personalizationStore.data.wallpaper.loading.googlePhotos.photos = false;
     personalizationStore.notifyObservers();
 
@@ -102,7 +104,9 @@
 
     assertWindowObjectsEqual(iframe.contentWindow, target);
     assertDeepEquals(
-        personalizationStore.data.wallpaper.googlePhotos.photos, data);
+        personalizationStore.data.wallpaper.googlePhotos.photos.slice(
+            0, kMaximumGooglePhotosPreviews),
+        data);
   });
 
   test('sends image counts when a collection loads', async () => {
diff --git a/chrome/test/data/webui/cr_components/BUILD.gn b/chrome/test/data/webui/cr_components/BUILD.gn
index b88712f..26da3a6 100644
--- a/chrome/test/data/webui/cr_components/BUILD.gn
+++ b/chrome/test/data/webui/cr_components/BUILD.gn
@@ -4,6 +4,8 @@
 
 import("//build/config/crypto.gni")
 import("//third_party/closure_compiler/compile_js.gni")
+import("//tools/typescript/ts_library.gni")
+import("//ui/webui/resources/tools/generate_grd.gni")
 
 js_type_check("closure_compile") {
   is_polymer3 = true
@@ -26,3 +28,28 @@
   ]
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
+
+generate_grd("build_grdp") {
+  grd_prefix = "webui_cr_components"
+  out_grd = "$target_gen_dir/resources.grdp"
+
+  deps = [ ":build_ts" ]
+  manifest_files = [ "$target_gen_dir/tsconfig.manifest" ]
+  resource_path_prefix = "cr_components"
+}
+
+ts_library("build_ts") {
+  root_dir = "./"
+  out_dir = "$target_gen_dir/tsc"
+  tsconfig_base = "tsconfig_base.json"
+  path_mappings = [ "chrome://webui-test/*|" +
+                    rebase_path("$root_gen_dir/chrome/test/data/webui/tsc/*",
+                                target_gen_dir) ]
+  in_files = [
+    "most_visited_focus_test.js",
+    "most_visited_test.js",
+    "most_visited_test_support.js",
+  ]
+  deps = [ "//ui/webui/resources/cr_components/most_visited:build_ts" ]
+  extra_deps = [ "..:generate_definitions" ]
+}
diff --git a/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js b/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js
index 5cf5ad722..1ec86c5 100644
--- a/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js
+++ b/chrome/test/data/webui/cr_components/cr_components_mojo_browsertest.js
@@ -34,7 +34,7 @@
 var CrComponentsMostVisitedTest = class extends CrComponentsMojoBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=cr_components/most_visited_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=cr_components/most_visited_test.js&host=webui-test';
   }
 };
 
diff --git a/chrome/test/data/webui/cr_components/cr_components_mojo_interactive_test.js b/chrome/test/data/webui/cr_components/cr_components_mojo_interactive_test.js
index 12e41fe67..09c7519 100644
--- a/chrome/test/data/webui/cr_components/cr_components_mojo_interactive_test.js
+++ b/chrome/test/data/webui/cr_components/cr_components_mojo_interactive_test.js
@@ -23,7 +23,7 @@
     class extends CrComponentsMojoInteractiveTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://new-tab-page/test_loader.html?module=cr_components/most_visited_focus_test.js';
+    return 'chrome://new-tab-page/test_loader.html?module=cr_components/most_visited_focus_test.js&host=webui-test';
   }
 };
 
diff --git a/chrome/test/data/webui/cr_components/most_visited_focus_test.js b/chrome/test/data/webui/cr_components/most_visited_focus_test.js
index 34c660e1..bdb6f86c 100644
--- a/chrome/test/data/webui/cr_components/most_visited_focus_test.js
+++ b/chrome/test/data/webui/cr_components/most_visited_focus_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '../mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {MostVisitedBrowserProxy} from 'chrome://resources/cr_components/most_visited/browser_proxy.js';
 import {MostVisitedElement} from 'chrome://resources/cr_components/most_visited/most_visited.js';
@@ -10,8 +10,8 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 
-import {TestBrowserProxy} from '../test_browser_proxy.js';
-import {eventToPromise} from '../test_util.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
 import {$$, assertFocus, keydown} from './most_visited_test_support.js';
 
diff --git a/chrome/test/data/webui/cr_components/most_visited_test.js b/chrome/test/data/webui/cr_components/most_visited_test.js
index dfdb024..f6b3e00e 100644
--- a/chrome/test/data/webui/cr_components/most_visited_test.js
+++ b/chrome/test/data/webui/cr_components/most_visited_test.js
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-import '../mojo_webui_test_support.js';
+import 'chrome://webui-test/mojo_webui_test_support.js';
 
 import {MostVisitedBrowserProxy} from 'chrome://resources/cr_components/most_visited/browser_proxy.js';
 import {MostVisitedElement} from 'chrome://resources/cr_components/most_visited/most_visited.js';
@@ -13,9 +13,9 @@
 import {loadTimeData} from 'chrome://resources/js/load_time_data.m.js';
 import {TextDirection} from 'chrome://resources/mojo/mojo/public/mojom/base/text_direction.mojom-webui.js';
 
-import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from '../chai_assert.js';
-import {TestBrowserProxy} from '../test_browser_proxy.js';
-import {eventToPromise, flushTasks} from '../test_util.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {TestBrowserProxy} from 'chrome://webui-test/test_browser_proxy.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
 
 import {$$, assertNotStyle, assertStyle, keydown} from './most_visited_test_support.js';
 
diff --git a/chrome/test/data/webui/cr_components/most_visited_test_support.js b/chrome/test/data/webui/cr_components/most_visited_test_support.js
index cacd0ce..a97976f 100644
--- a/chrome/test/data/webui/cr_components/most_visited_test_support.js
+++ b/chrome/test/data/webui/cr_components/most_visited_test_support.js
@@ -7,7 +7,7 @@
 import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 
-import {assertEquals, assertNotEquals} from '../chai_assert.js';
+import {assertEquals, assertNotEquals} from 'chrome://webui-test/chai_assert.js';
 
 /**
  * @param {!Element} element
diff --git a/chrome/test/data/webui/cr_components/tsconfig_base.json b/chrome/test/data/webui/cr_components/tsconfig_base.json
new file mode 100644
index 0000000..eeddfb3
--- /dev/null
+++ b/chrome/test/data/webui/cr_components/tsconfig_base.json
@@ -0,0 +1,9 @@
+{
+  "extends": "../../../../../tools/typescript/tsconfig_base.json",
+  "compilerOptions": {
+    "allowJs": true,
+    "typeRoots": [
+       "./../../../../../third_party/node/node_modules/@types"
+    ]
+  }
+}
diff --git a/chrome/test/data/webui/cr_elements/BUILD.gn b/chrome/test/data/webui/cr_elements/BUILD.gn
index 4f8252ca..af9532b 100644
--- a/chrome/test/data/webui/cr_elements/BUILD.gn
+++ b/chrome/test/data/webui/cr_elements/BUILD.gn
@@ -23,25 +23,17 @@
     ":cr_grid_focus_test",
     ":cr_icon_button_tests",
     ":cr_input_test",
-    ":cr_lazy_render_tests",
     ":cr_lottie_tests",
     ":cr_policy_indicator_behavior_tests",
     ":cr_policy_indicator_tests",
     ":cr_policy_pref_indicator_tests",
     ":cr_policy_strings",
     ":cr_profile_avatar_selector_tests",
-    ":cr_radio_button_test",
-    ":cr_radio_group_test",
     ":cr_scrollable_behavior_tests",
-    ":cr_search_field_tests",
-    ":cr_slider_test",
     ":cr_splitter_test",
-    ":cr_tabs_test",
     ":cr_toast_manager_test",
     ":cr_toast_test",
     ":cr_toolbar_focus_tests",
-    ":cr_toolbar_search_field_tests",
-    ":cr_toolbar_test",
     ":iron_list_focus_test",
   ]
 
@@ -101,16 +93,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("cr_lazy_render_tests") {
-  deps = [
-    "..:chai_assert",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_checkbox:cr_checkbox.m",
-    "//ui/webui/resources/cr_elements/cr_lazy_render:cr_lazy_render.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("cr_lottie_tests") {
   deps = [
     "..:chai_assert",
@@ -166,14 +148,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("cr_radio_button_test") {
-  deps = [
-    "..:chai_assert",
-    "//ui/webui/resources/cr_elements/cr_radio_button:cr_radio_button.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("cr_card_radio_button_test") {
   deps = [
     "..:chai_assert",
@@ -182,18 +156,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("cr_radio_group_test") {
-  deps = [
-    "..:chai_assert",
-    "..:test_util",
-    "//third_party/polymer/v3_0/components-chromium/iron-test-helpers:mock-interactions",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_radio_button:cr_radio_button.m",
-    "//ui/webui/resources/cr_elements/cr_radio_group:cr_radio_group.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("cr_scrollable_behavior_tests") {
   deps = [
     "..:chai_assert",
@@ -205,27 +167,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("cr_search_field_tests") {
-  deps = [
-    "..:chai_assert",
-    "..:test_util",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_search_field:cr_search_field",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
-js_library("cr_slider_test") {
-  deps = [
-    "..:chai_assert",
-    "..:test_util",
-    "//third_party/polymer/v3_0/components-chromium/iron-test-helpers:mock-interactions",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_slider:cr_slider",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("cr_splitter_test") {
   deps = [
     "..:chai_assert",
@@ -234,17 +175,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("cr_tabs_test") {
-  deps = [
-    "..:chai_assert",
-    "..:test_util",
-    "//third_party/polymer/v3_0/components-chromium/iron-test-helpers:mock-interactions",
-    "//ui/webui/resources/cr_elements/cr_tabs:cr_tabs",
-    "//ui/webui/resources/js:util.m",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("cr_toast_manager_test") {
   deps = [
     "..:chai_assert",
@@ -264,16 +194,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("cr_toolbar_search_field_tests") {
-  deps = [
-    "..:chai_assert",
-    "//third_party/polymer/v3_0/components-chromium/iron-test-helpers:mock-interactions",
-    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
-    "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar_search_field",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("cr_toolbar_focus_tests") {
   deps = [
     "..:chai_assert",
@@ -284,15 +204,6 @@
   externs_list = [ "$externs_path/mocha-2.5.js" ]
 }
 
-js_library("cr_toolbar_test") {
-  deps = [
-    "..:chai_assert",
-    "..:test_util",
-    "//ui/webui/resources/cr_elements/cr_toolbar:cr_toolbar",
-  ]
-  externs_list = [ "$externs_path/mocha-2.5.js" ]
-}
-
 js_library("iron_list_focus_test") {
   deps = [
     "..:chai_assert",
@@ -338,8 +249,16 @@
     "cr_dialog_test.ts",
     "cr_drawer_tests.ts",
     "cr_expand_button_tests.ts",
+    "cr_lazy_render_tests.ts",
     "cr_link_row_tests.ts",
+    "cr_radio_group_test.ts",
+    "cr_radio_button_test.ts",
+    "cr_search_field_tests.ts",
+    "cr_slider_test.ts",
+    "cr_tabs_test.ts",
     "cr_toggle_test.ts",
+    "cr_toolbar_search_field_tests.ts",
+    "cr_toolbar_test.ts",
     "cr_view_manager_test.ts",
   ]
   deps = [ "//ui/webui/resources:library" ]
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
index ffc3f64..bb65112d 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_browsertest.js
@@ -172,7 +172,7 @@
 var CrElementsLazyRenderTest = class extends CrElementsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_lazy_render_tests.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_lazy_render_tests.js&host=webui-test';
   }
 };
 
@@ -194,7 +194,7 @@
 var CrElementsRadioButtonTest = class extends CrElementsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_radio_button_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_radio_button_test.js&host=webui-test';
   }
 };
 
@@ -217,7 +217,7 @@
 var CrElementsRadioGroupTest = class extends CrElementsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_radio_group_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_radio_group_test.js&host=webui-test';
   }
 };
 
@@ -239,7 +239,7 @@
 var CrElementsSearchFieldTest = class extends CrElementsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_search_field_tests.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_search_field_tests.js&host=webui-test';
   }
 };
 
@@ -263,7 +263,7 @@
 var CrElementsSliderTest = class extends CrElementsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_slider_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_slider_test.js&host=webui-test';
   }
 };
 
@@ -380,7 +380,7 @@
 var CrElementsToolbarTest = class extends CrElementsBrowserTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_toolbar_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_toolbar_test.js&host=webui-test';
   }
 };
 
diff --git a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
index 051d6f6..1ae09c4 100644
--- a/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_elements_focus_test.js
@@ -76,7 +76,7 @@
 var CrElementsTabsTest = class extends CrElementsFocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_tabs_test.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_tabs_test.js&host=webui-test';
   }
 };
 
@@ -98,7 +98,7 @@
 var CrElementsToolbarSearchFieldTest = class extends CrElementsFocusTest {
   /** @override */
   get browsePreload() {
-    return 'chrome://test/test_loader.html?module=cr_elements/cr_toolbar_search_field_tests.js';
+    return 'chrome://test/test_loader.html?module=cr_elements/cr_toolbar_search_field_tests.js&host=webui-test';
   }
 };
 
diff --git a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.ts
similarity index 70%
rename from chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
rename to chrome/test/data/webui/cr_elements/cr_lazy_render_tests.ts
index b14ae14c..1241a6fa 100644
--- a/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_lazy_render_tests.ts
@@ -6,20 +6,18 @@
 import 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.m.js';
 import 'chrome://resources/cr_elements/cr_checkbox/cr_checkbox.m.js';
 
-import {html, Polymer} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {CrLazyRenderElement} from 'chrome://resources/cr_elements/cr_lazy_render/cr_lazy_render.m.js';
 
-import {assertEquals, assertFalse, assertNotEquals, assertTrue} from '../chai_assert.js';
+import {assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 
 // clang-format on
 
 suite('cr-lazy-render', function() {
-  /** @type {!CrLazyRenderElement} */
-  let lazy;
+  let lazy: CrLazyRenderElement<HTMLElement>;
 
-  let bind;
+  type BindData = {name: string, checked: boolean};
 
-  suiteSetup(function() {
-  });
+  let bind: HTMLElement&BindData;
 
   setup(function() {
     const template = `
@@ -36,9 +34,8 @@
           </template>
         </dom-bind>`;
     document.body.innerHTML = template;
-    lazy =
-        /** @type {!CrLazyRenderElement} */ (document.getElementById('lazy'));
-    bind = document.querySelector('dom-bind');
+    lazy = document.body.querySelector('cr-lazy-render')!;
+    bind = document.body.querySelector<HTMLElement&BindData>('dom-bind')!;
   });
 
   test('stamps after get()', function() {
@@ -54,16 +51,16 @@
     bind.name = 'Wings';
 
     const inner = lazy.get();
-    assertNotEquals(-1, inner.textContent.indexOf('Wings'));
+    assertNotEquals(-1, inner.textContent!.indexOf('Wings'));
     bind.name = 'DC';
-    assertNotEquals(-1, inner.textContent.indexOf('DC'));
+    assertNotEquals(-1, inner.textContent!.indexOf('DC'));
   });
 
   test('two-way binding works', function() {
     bind.checked = true;
 
-    const inner = lazy.get();
-    const checkbox = document.querySelector('cr-checkbox');
+    lazy.get();
+    const checkbox = document.querySelector('cr-checkbox')!;
     assertTrue(checkbox.checked);
     checkbox.click();
     assertFalse(checkbox.checked);
diff --git a/chrome/test/data/webui/cr_elements/cr_radio_button_test.js b/chrome/test/data/webui/cr_elements/cr_radio_button_test.ts
similarity index 72%
rename from chrome/test/data/webui/cr_elements/cr_radio_button_test.js
rename to chrome/test/data/webui/cr_elements/cr_radio_button_test.ts
index cac6035..e26ee5f 100644
--- a/chrome/test/data/webui/cr_elements/cr_radio_button_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_radio_button_test.ts
@@ -4,17 +4,17 @@
 
 // clang-format off
 import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
-import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
+
+import {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
 
 suite('cr-radio-button', function() {
-  /** @type {!CrRadioButtonElement} */
-  let radioButton;
+  let radioButton: CrRadioButtonElement;
 
   setup(function() {
     document.body.innerHTML = '';
-    radioButton = /** @type {!CrRadioButtonElement} */ (
-        document.createElement('cr-radio-button'));
+    radioButton = document.createElement('cr-radio-button');
     document.body.appendChild(radioButton);
   });
 
@@ -22,7 +22,7 @@
     assertTrue(radioButton.hasAttribute('checked'));
     assertEquals('true', radioButton.$.button.getAttribute('aria-checked'));
     assertTrue(
-        getComputedStyle(radioButton.$$('.disc')).backgroundColor !==
+        getComputedStyle(radioButton.$$('.disc')!).backgroundColor !==
         'rgba(0, 0, 0, 0)');
   }
 
@@ -31,7 +31,7 @@
     assertEquals('false', radioButton.$.button.getAttribute('aria-checked'));
     assertEquals(
         'rgba(0, 0, 0, 0)',
-        getComputedStyle(radioButton.$$('.disc')).backgroundColor);
+        getComputedStyle(radioButton.$$('.disc')!).backgroundColor);
   }
 
   function assertDisabled() {
@@ -67,11 +67,12 @@
   });
 
   test('Ripple', function() {
-    assertFalse(!!radioButton.$$('paper-ripple'));
+    assertFalse(!!radioButton.shadowRoot!.querySelector('paper-ripple'));
     radioButton.fire('focus');
-    assertTrue(!!radioButton.$$('paper-ripple'));
-    assertTrue(radioButton.$$('paper-ripple').holdDown);
+    assertTrue(!!radioButton.shadowRoot!.querySelector('paper-ripple'));
+    assertTrue(radioButton.shadowRoot!.querySelector('paper-ripple')!.holdDown);
     radioButton.fire('up');
-    assertFalse(radioButton.$$('paper-ripple').holdDown);
+    assertFalse(
+        radioButton.shadowRoot!.querySelector('paper-ripple')!.holdDown);
   });
 });
diff --git a/chrome/test/data/webui/cr_elements/cr_radio_group_test.js b/chrome/test/data/webui/cr_elements/cr_radio_group_test.ts
similarity index 69%
rename from chrome/test/data/webui/cr_elements/cr_radio_group_test.js
rename to chrome/test/data/webui/cr_elements/cr_radio_group_test.ts
index 9396478..7ff9a33 100644
--- a/chrome/test/data/webui/cr_elements/cr_radio_group_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_radio_group_test.ts
@@ -6,20 +6,17 @@
 import 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
 import 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
 
+import {CrRadioButtonElement} from 'chrome://resources/cr_elements/cr_radio_button/cr_radio_button.m.js';
+import {CrRadioGroupElement} from 'chrome://resources/cr_elements/cr_radio_group/cr_radio_group.m.js';
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise} from 'chrome://webui-test/test_util.js';
 
-import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
-import {eventToPromise} from '../test_util.js';
 // clang-format on
 
 suite('cr-radio-group', () => {
-  /** @type {!CrRadioGroupElement} */
-  let radioGroup;
-
-  /** @override */
-  suiteSetup(() => {
-  });
+  let radioGroup: CrRadioGroupElement;
 
   setup(() => {
     document.body.innerHTML = `
@@ -28,26 +25,18 @@
           <cr-radio-button name="2"><input></input></cr-radio-button>
           <cr-radio-button name="3"><a></a></cr-radio-button>
         </cr-radio-group>`;
-    radioGroup = /** @type {!CrRadioGroupElement} */ (
-        document.body.querySelector('cr-radio-group'));
+    radioGroup = document.body.querySelector('cr-radio-group')!;
     flush();
   });
 
-
-  /**
-   * @param {number} length
-   * @param {string} selector
-   */
-  function checkLength(length, selector) {
+  function checkLength(length: number, selector: string) {
     assertEquals(length, radioGroup.querySelectorAll(selector).length);
   }
 
-  /**
-   * @param {string} name
-   */
-  function verifyNoneSelectedOneFocusable(name) {
-    const uncheckedRows = Array.from(
-        radioGroup.querySelectorAll(`cr-radio-button:not([checked])`));
+  function verifyNoneSelectedOneFocusable(name: string) {
+    const uncheckedRows =
+        Array.from(radioGroup.querySelectorAll<CrRadioButtonElement>(
+            `cr-radio-button:not([checked])`));
     assertEquals(3, uncheckedRows.length);
 
     const focusableRow = uncheckedRows.filter(
@@ -69,43 +58,33 @@
     assertEquals(3, unfocusableRows.length);
   }
 
-  /**
-   * @param {string} key
-   * @param {!Element=} target
-   */
-  function press(key, target) {
-    target = target || /** @type {!CrRadioButtonElement} */
-        (radioGroup.querySelector('[name="1"]'));
-    pressAndReleaseKeyOn(target, -1, [], key);
+  function press(key: string, target?: Element) {
+    pressAndReleaseKeyOn(
+        target || radioGroup.querySelector('[name="1"]')!, -1, [], key);
   }
 
-  /**
-   * @param {!Array<string>} keys
-   * @param {string} initialSelection
-   * @param {string} expectedSelected
-   */
-  function checkPressed(keys, initialSelection, expectedSelected) {
-    keys.forEach((key, i) => {
+  function checkPressed(
+      keys: string[], initialSelection: string, expectedSelected: string) {
+    keys.forEach(key => {
       radioGroup.selected = initialSelection;
       press(key);
       checkSelected(expectedSelected);
     });
   }
 
-  /**
-   * @param {string} name
-   */
-  function checkSelected(name) {
+  function checkSelected(name: string) {
     assertEquals(name, `${radioGroup.selected}`);
 
-    const selectedRows = Array.from(radioGroup.querySelectorAll(
-        `cr-radio-button[name="${name}"][checked]`));
+    const selectedRows =
+        Array.from(radioGroup.querySelectorAll<CrRadioButtonElement>(
+            `cr-radio-button[name="${name}"][checked]`));
     const focusableRows =
         selectedRows.filter(radioButton => radioButton.$.button.tabIndex === 0);
     assertEquals(1, focusableRows.length);
 
-    const unselectedRows = Array.from(radioGroup.querySelectorAll(
-        `cr-radio-button:not([name="${name}"]):not([checked])`));
+    const unselectedRows =
+        Array.from(radioGroup.querySelectorAll<CrRadioButtonElement>(
+            `cr-radio-button:not([name="${name}"]):not([checked])`));
     const filteredUnselected = unselectedRows.filter(
         radioButton => radioButton.$.button.tabIndex === -1);
     assertEquals(2, filteredUnselected.length);
@@ -118,7 +97,6 @@
   });
 
   test('key events when initially nothing checked', () => {
-    const firstRadio = radioGroup.querySelector('[name="1"]');
     press('Enter');
     checkSelected('1');
     radioGroup.selected = '';
@@ -150,13 +128,14 @@
 
   test('mouse event', () => {
     assertEquals(undefined, radioGroup.selected);
-    radioGroup.querySelector('[name="2"]').click();
+    radioGroup.querySelector<CrRadioButtonElement>('[name="2"]')!.click();
     checkSelected('2');
   });
 
   test('key events skip over disabled radios', () => {
     verifyNoneSelectedOneFocusable('1');
-    radioGroup.querySelector('[name="2"]').disabled = true;
+    radioGroup.querySelector<CrRadioButtonElement>('[name="2"]')!.disabled =
+        true;
     press('PageDown');
     checkSelected('3');
   });
@@ -168,10 +147,12 @@
     checkNoneFocusable();
     radioGroup.disabled = false;
     checkSelected('1');
-    const firstRadio = radioGroup.querySelector('[name="1"]');
+    const firstRadio =
+        radioGroup.querySelector<CrRadioButtonElement>('[name="1"]')!;
     firstRadio.disabled = true;
     assertEquals(-1, firstRadio.$.button.tabIndex);
-    const secondRadio = radioGroup.querySelector('[name="2"]');
+    const secondRadio =
+        radioGroup.querySelector<CrRadioButtonElement>('[name="2"]')!;
     assertEquals(0, secondRadio.$.button.tabIndex);
     firstRadio.disabled = false;
     checkSelected('1');
@@ -194,7 +175,8 @@
 
     // Check that if a button already disabled, it will remain disabled after
     // group is re-enabled.
-    const firstRadio = radioGroup.querySelector('[name="1"]');
+    const firstRadio =
+        radioGroup.querySelector<CrRadioButtonElement>('[name="1"]')!;
     firstRadio.disabled = true;
     checkLength(2, '[aria-disabled="false"]');
     checkLength(1, '[aria-disabled="true"][disabled][name="1"]');
@@ -209,63 +191,59 @@
   test('radios name change updates selection and tabindex', () => {
     radioGroup.selected = '1';
     checkSelected('1');
-    const firstRadio = radioGroup.querySelector('[name="1"]');
+    const firstRadio =
+        radioGroup.querySelector<CrRadioButtonElement>('[name="1"]')!;
     firstRadio.name = 'A';
     assertEquals(0, firstRadio.$.button.tabIndex);
     assertFalse(firstRadio.checked);
     verifyNoneSelectedOneFocusable('A');
-    const secondRadio = radioGroup.querySelector('[name="2"]');
-    radioGroup.querySelector('[name="2"]').name = '1';
+    radioGroup.querySelector<CrRadioButtonElement>('[name="2"]')!.name = '1';
     checkSelected('1');
   });
 
   test('radios with links', () => {
     const a = radioGroup.querySelector('a');
     assertTrue(!!a);
-    assertEquals(-1, a.tabIndex);
+    assertEquals(-1, a!.tabIndex);
     verifyNoneSelectedOneFocusable('1');
-    press('Enter', a);
-    press(' ', a);
-    a.click();
+    press('Enter', a!);
+    press(' ', a!);
+    a!.click();
     verifyNoneSelectedOneFocusable('1');
-    radioGroup.querySelector('[name="1"]').click();
+    radioGroup.querySelector<CrRadioButtonElement>('[name="1"]')!.click();
     checkSelected('1');
-    press('Enter', a);
-    press(' ', a);
-    a.click();
+    press('Enter', a!);
+    press(' ', a!);
+    a!.click();
     checkSelected('1');
-    radioGroup.querySelector('[name="3"]').click();
+    radioGroup.querySelector<CrRadioButtonElement>('[name="3"]')!.click();
     checkSelected('3');
-    assertEquals(0, a.tabIndex);
+    assertEquals(0, a!.tabIndex);
   });
 
   test('radios with input', () => {
-    const input =
-        /** @type {!HTMLInputElement} */ (radioGroup.querySelector('input'));
+    const input = radioGroup.querySelector('input');
     assertTrue(!!input);
     verifyNoneSelectedOneFocusable('1');
-    press('Enter', input);
-    press(' ', input);
+    press('Enter', input!);
+    press(' ', input!);
     verifyNoneSelectedOneFocusable('1');
-    input.click();
+    input!.click();
     checkSelected('2');
-    radioGroup.querySelector('[name="1"]').click();
-    press('Enter', input);
-    press(' ', input);
+    radioGroup.querySelector<CrRadioButtonElement>('[name="1"]')!.click();
+    press('Enter', input!);
+    press(' ', input!);
     checkSelected('1');
-    input.click();
+    input!.click();
     checkSelected('2');
   });
 
   test('select the radio that has focus when space or enter pressed', () => {
     verifyNoneSelectedOneFocusable('1');
     press(
-        'Enter', /** @type {!CrRadioButtonElement} */
-        (radioGroup.querySelector('[name="3"]')));
+        'Enter', radioGroup.querySelector<CrRadioButtonElement>('[name="3"]')!);
     checkSelected('3');
-    press(
-        ' ', /** @type {!CrRadioButtonElement} */
-        (radioGroup.querySelector('[name="2"]')));
+    press(' ', radioGroup.querySelector<CrRadioButtonElement>('[name="2"]')!);
     checkSelected('2');
   });
 });
diff --git a/chrome/test/data/webui/cr_elements/cr_search_field_tests.js b/chrome/test/data/webui/cr_elements/cr_search_field_tests.ts
similarity index 79%
rename from chrome/test/data/webui/cr_elements/cr_search_field_tests.js
rename to chrome/test/data/webui/cr_elements/cr_search_field_tests.ts
index b5d94e8..8955ce6 100644
--- a/chrome/test/data/webui/cr_elements/cr_search_field_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_search_field_tests.ts
@@ -3,25 +3,23 @@
 // found in the LICENSE file.
 
 // clang-format off
+import 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js';
+
 import {CrSearchFieldElement} from 'chrome://resources/cr_elements/cr_search_field/cr_search_field.js';
 
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertNotReached, assertTrue} from '../chai_assert.js';
-import {flushTasks} from '../test_util.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertNotReached, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {flushTasks} from 'chrome://webui-test/test_util.js';
 // clang-format on
 
 /** @fileoverview Suite of tests for cr-search-field. */
 suite('cr-search-field', function() {
-  /** @type {!CrSearchFieldElement} */
-  let field;
+  let field: CrSearchFieldElement;
+  let searches: string[]|null = null;
 
-  /** @type {?Array<string>} */
-  let searches = null;
-
-  /** @param {string} term */
-  function simulateSearch(term) {
-    field.shadowRoot.querySelector('#searchInput').value = term;
+  function simulateSearch(term: string) {
+    field.$.searchInput.value = term;
     field.onSearchTermInput();
     field.onSearchTermSearch();
   }
@@ -33,11 +31,10 @@
     const base = document.createElement('base');
     base.href = 'chrome://resources/cr_elements/';
     document.head.appendChild(base);
-    field = /** @type {!CrSearchFieldElement} */ (
-        document.createElement('cr-search-field'));
+    field = document.createElement('cr-search-field');
     searches = [];
     field.addEventListener('search-changed', function(event) {
-      searches.push(event.detail);
+      searches!.push((event as CustomEvent<string>).detail);
     });
     document.body.appendChild(field);
   });
@@ -68,12 +65,10 @@
     flush();
     assertTrue(field.hasSearchText);
 
-    field.shadowRoot.querySelector('#clearSearch').click();
+    field.$.clearSearch.click();
     assertEquals('', field.getValue());
     await flushTasks();
-    assertEquals(
-        field.shadowRoot.querySelector('#searchInput'),
-        field.root.activeElement);
+    assertEquals(field.$.searchInput, field.shadowRoot!.activeElement);
     assertFalse(field.hasSearchText);
   });
 
@@ -84,12 +79,10 @@
     flush();
     assertTrue(field.hasSearchText);
 
-    field.shadowRoot.querySelector('#clearSearch').click();
+    field.$.clearSearch.click();
     assertEquals('', field.getValue());
     await flushTasks();
-    assertEquals(
-        field.shadowRoot.querySelector('#searchInput'),
-        field.root.activeElement);
+    assertEquals(field.$.searchInput, field.shadowRoot!.activeElement);
     assertFalse(field.hasSearchText);
   });
 
@@ -99,7 +92,7 @@
     flush();
     assertEquals('query1', field.getValue());
 
-    field.shadowRoot.querySelector('#clearSearch').click();
+    field.$.clearSearch.click();
     assertEquals('', field.getValue());
 
     simulateSearch('query2');
@@ -137,6 +130,6 @@
     field.setValue(value, true);
     field.setValue(`  ${value}  `);
     assertTrue(calledSetValue);
-    assertEquals(0, searches.length);
+    assertEquals(0, searches!.length);
   });
 });
diff --git a/chrome/test/data/webui/cr_elements/cr_slider_test.js b/chrome/test/data/webui/cr_elements/cr_slider_test.ts
similarity index 82%
rename from chrome/test/data/webui/cr_elements/cr_slider_test.js
rename to chrome/test/data/webui/cr_elements/cr_slider_test.ts
index 77e7a98..a68a12c 100644
--- a/chrome/test/data/webui/cr_elements/cr_slider_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_slider_test.ts
@@ -3,18 +3,19 @@
 // found in the LICENSE file.
 
 // clang-format off
+import 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
+
 import {CrSliderElement} from 'chrome://resources/cr_elements/cr_slider/cr_slider.js';
 
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
 
-import {assertEquals, assertFalse, assertTrue} from '../chai_assert.js';
-import {eventToPromise, flushTasks} from '../test_util.js';
+import {assertEquals, assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
 // clang-format on
 
 suite('cr-slider', function() {
-  /** @type {!CrSliderElement} */
-  let crSlider;
+  let crSlider: CrSliderElement;
 
   setup(function() {
     document.body.innerHTML = `
@@ -28,17 +29,16 @@
       </div>
     `;
 
-    crSlider = /** @type {!CrSliderElement} */ (
-        document.body.querySelector('cr-slider'));
+    crSlider = document.body.querySelector('cr-slider')!;
     crSlider.value = 0;
     return flushTasks();
   });
 
-  /** @param {boolean} expected */
-  function checkDisabled(expected) {
+  function checkDisabled(expected: boolean) {
     assertEquals(
         expected,
-        window.getComputedStyle(crSlider)['pointer-events'] === 'none');
+        window.getComputedStyle(crSlider).getPropertyValue('pointer-events') ===
+            'none');
     const expectedTabindex = expected ? '-1' : '0';
     assertEquals(expectedTabindex, crSlider.getAttribute('tabindex'));
   }
@@ -75,9 +75,9 @@
     pressAndReleaseKeyOn(crSlider, 35, [], 'End');
   }
 
-  function pointerEvent(eventType, ratio) {
-    const rect =
-        crSlider.shadowRoot.querySelector('#container').getBoundingClientRect();
+  function pointerEvent(eventType: string, ratio: number) {
+    const rect = crSlider.shadowRoot!.querySelector(
+                                         '#container')!.getBoundingClientRect();
     crSlider.dispatchEvent(new PointerEvent(eventType, {
       buttons: 1,
       pointerId: 1,
@@ -85,11 +85,11 @@
     }));
   }
 
-  function pointerDown(ratio) {
+  function pointerDown(ratio: number) {
     pointerEvent('pointerdown', ratio);
   }
 
-  function pointerMove(ratio) {
+  function pointerMove(ratio: number) {
     pointerEvent('pointermove', ratio);
   }
 
@@ -191,13 +191,16 @@
   });
 
   test('markers', () => {
-    assertTrue(crSlider.shadowRoot.querySelector('#markers').hidden);
+    const markersElement =
+        crSlider.shadowRoot!.querySelector<HTMLElement>('#markers')!;
+    assertTrue(markersElement.hidden);
     crSlider.markerCount = 10;
-    assertFalse(crSlider.shadowRoot.querySelector('#markers').hidden);
+    assertFalse(markersElement.hidden);
     flush();
-    const markers = Array.from(crSlider.root.querySelectorAll('#markers div'));
+    const markers =
+        Array.from(crSlider.shadowRoot!.querySelectorAll('#markers div'));
     assertEquals(9, markers.length);
-    markers.forEach((marker, i) => {
+    markers.forEach(marker => {
       assertTrue(marker.classList.contains('inactive-marker'));
     });
     crSlider.value = 100;
@@ -221,14 +224,14 @@
     assertEquals('4', crSlider.getAttribute('aria-valuetext'));
     assertEquals('4', crSlider.getAttribute('aria-valuenow'));
     assertEquals(
-        '', crSlider.shadowRoot.querySelector('#label').innerHTML.trim());
+        '', crSlider.shadowRoot!.querySelector('#label')!.innerHTML.trim());
     assertEquals(2, crSlider.value);
     pressArrowRight();
     assertEquals(3, crSlider.value);
     assertEquals('8', crSlider.getAttribute('aria-valuetext'));
     assertEquals('8', crSlider.getAttribute('aria-valuenow'));
     assertEquals(
-        '', crSlider.shadowRoot.querySelector('#label').innerHTML.trim());
+        '', crSlider.shadowRoot!.querySelector('#label')!.innerHTML.trim());
     crSlider.value = 2;
     crSlider.ticks = [
       {
@@ -250,13 +253,15 @@
     assertEquals('3', crSlider.getAttribute('aria-valuemax'));
     assertEquals('Third', crSlider.getAttribute('aria-valuetext'));
     assertEquals(
-        'Third', crSlider.shadowRoot.querySelector('#label').innerHTML.trim());
+        'Third',
+        crSlider.shadowRoot!.querySelector('#label')!.innerHTML.trim());
     assertEquals('3', crSlider.getAttribute('aria-valuenow'));
     pressArrowLeft();
     assertEquals('Second', crSlider.getAttribute('aria-valuetext'));
     assertEquals('20', crSlider.getAttribute('aria-valuenow'));
     assertEquals(
-        'Second', crSlider.shadowRoot.querySelector('#label').innerHTML.trim());
+        'Second',
+        crSlider.shadowRoot!.querySelector('#label')!.innerHTML.trim());
   });
 
   test('disabled whenever public |disabled| is true', () => {
@@ -316,9 +321,9 @@
   });
 
   test('value updated before dragging-changed event handled', () => {
-    const wait = new Promise(resolve => {
+    const wait = new Promise<void>(resolve => {
       crSlider.addEventListener('dragging-changed', e => {
-        if (!e.detail.value) {
+        if (!(e as CustomEvent<{value: number}>).detail.value) {
           assertEquals(50, crSlider.value);
           resolve();
         }
@@ -331,36 +336,39 @@
   });
 
   test('smooth position transition only on pointerdown', async () => {
-    const assertNoTransition = () => {
+    function assertNoTransition() {
       const expected = 'all 0s ease 0s';
       assertEquals(
           expected,
-          getComputedStyle(crSlider.shadowRoot.querySelector('#knobAndLabel'))
+          getComputedStyle(crSlider.shadowRoot!.querySelector('#knobAndLabel')!)
               .transition);
       assertEquals(
           expected,
-          getComputedStyle(crSlider.shadowRoot.querySelector('#bar'))
+          getComputedStyle(crSlider.shadowRoot!.querySelector('#bar')!)
               .transition);
-    };
-    const assertTransition = () => {
-      const getValue = propName => `${propName} 0.08s ease 0s`;
+    }
+
+    function assertTransition() {
+      function getValue(propName: string) {
+        return `${propName} 0.08s ease 0s`;
+      }
+
       assertEquals(
           getValue('margin-inline-start'),
-          getComputedStyle(crSlider.shadowRoot.querySelector('#knobAndLabel'))
+          getComputedStyle(crSlider.shadowRoot!.querySelector('#knobAndLabel')!)
               .transition);
       assertEquals(
           getValue('width'),
-          getComputedStyle(crSlider.shadowRoot.querySelector('#bar'))
+          getComputedStyle(crSlider.shadowRoot!.querySelector('#bar')!)
               .transition);
-    };
+    }
 
     assertNoTransition();
     pointerDown(.5);
     assertTransition();
 
     const knobAndLabel =
-        /** @type {!HTMLElement} */ (
-            crSlider.shadowRoot.querySelector('#knobAndLabel'));
+        crSlider.shadowRoot!.querySelector<HTMLElement>('#knobAndLabel')!;
 
     await eventToPromise('transitionend', knobAndLabel);
     assertNoTransition();
@@ -438,11 +446,12 @@
 
   test('container hidden until value set', async () => {
     document.body.innerHTML = '<cr-slider></cr-slider>';
-    crSlider = /** @type {!CrSliderElement} */ (
-        document.body.querySelector('cr-slider'));
-    assertTrue(crSlider.shadowRoot.querySelector('#container').hidden);
+    crSlider = document.body.querySelector('cr-slider')!;
+    assertTrue(
+        crSlider.shadowRoot!.querySelector<HTMLElement>('#container')!.hidden);
     crSlider.value = 0;
     await flushTasks();
-    assertFalse(crSlider.shadowRoot.querySelector('#container').hidden);
+    assertFalse(
+        crSlider.shadowRoot!.querySelector<HTMLElement>('#container')!.hidden);
   });
 });
diff --git a/chrome/test/data/webui/cr_elements/cr_tabs_test.js b/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
similarity index 72%
rename from chrome/test/data/webui/cr_elements/cr_tabs_test.js
rename to chrome/test/data/webui/cr_elements/cr_tabs_test.ts
index 93b4112..f2c0b9a 100644
--- a/chrome/test/data/webui/cr_elements/cr_tabs_test.js
+++ b/chrome/test/data/webui/cr_elements/cr_tabs_test.ts
@@ -3,43 +3,34 @@
 // found in the LICENSE file.
 
 // clang-format off
+import 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
+
 import {CrTabsElement} from 'chrome://resources/cr_elements/cr_tabs/cr_tabs.js';
 
-import {getDeepActiveElement} from 'chrome://resources/js/util.m.js';
 import {keyDownOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 
-import {assertEquals, assertNotEquals, assertTrue} from '../chai_assert.js';
-import {eventToPromise, flushTasks} from '../test_util.js';
+import {assertEquals, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {eventToPromise, flushTasks} from 'chrome://webui-test/test_util.js';
 // clang-format on
 
 suite('cr_tabs_test', function() {
-  /** @type {!CrTabsElement} */
-  let tabs;
+  let tabs: CrTabsElement;
 
   setup(() => {
     document.body.innerHTML = '';
-    tabs = /** @type {!CrTabsElement} */ (document.createElement('cr-tabs'));
+    tabs = document.createElement('cr-tabs');
     tabs.tabNames = ['tab1', 'tab2', 'tab3'];
     tabs.tabIcons = ['chrome://icon1.png'];
     document.body.appendChild(tabs);
     return flushTasks();
   });
 
-  /**
-   * @param {number} index
-   * @return {!HTMLElement}
-   */
-  function getTabElement(index) {
-    return /** @type {!HTMLElement} */ (
-        tabs.shadowRoot.querySelector(`.tab:nth-of-type(${index + 1})`));
+  function getTabElement(index: number): HTMLElement {
+    return tabs.shadowRoot!.querySelector(`.tab:nth-of-type(${index + 1})`)!;
   }
 
-  /**
-   * @param {Function} uiChange
-   * @param {number} initialSelection
-   * @param {number} expectedSelection
-   */
-  async function checkUiChange(uiChange, initialSelection, expectedSelection) {
+  async function checkUiChange(
+      uiChange: Function, initialSelection: number, expectedSelection: number) {
     tabs.selected = initialSelection;
     if (initialSelection === expectedSelection) {
       uiChange();
@@ -53,28 +44,22 @@
     assertTrue(!!tabElement);
     assertTrue(tabElement.classList.contains('selected'));
     assertEquals('0', tabElement.getAttribute('tabindex'));
-    const notSelected = tabs.shadowRoot.querySelectorAll('.tab:not(.selected)');
+    const notSelected =
+        tabs.shadowRoot!.querySelectorAll('.tab:not(.selected)');
     assertEquals(2, notSelected.length);
     notSelected.forEach(tab => {
       assertEquals('-1', tab.getAttribute('tabindex'));
     });
   }
 
-  /**
-   * @param {string} key
-   * @param {number} initialSelection
-   * @param {number} expectedSelection
-   */
-  async function checkKey(key, initialSelection, expectedSelection) {
+  async function checkKey(
+      key: string, initialSelection: number, expectedSelection: number) {
     await checkUiChange(
         () => keyDownOn(tabs, 0, [], key), initialSelection, expectedSelection);
   }
 
-  /**
-   * @param {number} initialSelection
-   * @param {number} expectedSelection
-   */
-  async function checkClickTab(initialSelection, expectedSelection) {
+  async function checkClickTab(
+      initialSelection: number, expectedSelection: number) {
     await checkUiChange(
         () => getTabElement(expectedSelection).click(), initialSelection,
         expectedSelection);
@@ -98,11 +83,11 @@
 
   test('tab icons are optional', () => {
     const tab0 = getTabElement(0);
-    const tabIcon0 = tab0.querySelector('.tab-icon');
+    const tabIcon0 = tab0.querySelector('.tab-icon')!;
     assertNotEquals('none', getComputedStyle(tabIcon0).display);
 
     const tab1 = getTabElement(1);
-    const tabIcon1 = tab1.querySelector('.tab-icon');
+    const tabIcon1 = tab1.querySelector('.tab-icon')!;
     assertEquals('none', getComputedStyle(tabIcon1).display);
   });
 
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.ts
similarity index 83%
rename from chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
rename to chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.ts
index b51f7ac98..a793d1e 100644
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.js
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_search_field_tests.ts
@@ -3,35 +3,32 @@
 // found in the LICENSE file.
 
 // clang-format off
+import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
+
 import {CrToolbarSearchFieldElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js';
 
 import {pressAndReleaseKeyOn} from 'chrome://resources/polymer/v3_0/iron-test-helpers/mock-interactions.js';
 import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
-import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from '../chai_assert.js';
+import {assertDeepEquals, assertEquals, assertFalse, assertNotEquals, assertTrue} from 'chrome://webui-test/chai_assert.js';
 // clang-format on
 
 /** @fileoverview Suite of tests for cr-toolbar-search-field. */
 suite('cr-toolbar-search-field', function() {
-  /** @type {!CrToolbarSearchFieldElement} */
-  let field;
+  let field: CrToolbarSearchFieldElement;
+  let searches: string[]|null = null;
 
-  /** @type {?Array<string>} */
-  let searches = null;
-
-  /** @param {string} term */
-  function simulateSearch(term) {
-    field.shadowRoot.querySelector('#searchInput').value = term;
+  function simulateSearch(term: string) {
+    field.$.searchInput.value = term;
     field.onSearchTermInput();
     field.onSearchTermSearch();
   }
 
   setup(function() {
     document.body.innerHTML = '';
-    field = /** @type {!CrToolbarSearchFieldElement} */ (
-        document.createElement('cr-toolbar-search-field'));
+    field = document.createElement('cr-toolbar-search-field');
     searches = [];
     field.addEventListener('search-changed', function(event) {
-      searches.push(event.detail);
+      searches!.push((event as CustomEvent<string>).detail);
     });
     document.body.appendChild(field);
   });
@@ -59,19 +56,18 @@
     assertFalse(field.showingSearch);
     field.click();
     assertTrue(field.showingSearch);
-    const searchInput = /** @type {!HTMLElement} */ (
-        field.shadowRoot.querySelector('#searchInput'));
-    assertEquals(searchInput, field.root.activeElement);
+    const searchInput = /** @type {!HTMLElement} */ (field.$.searchInput);
+    assertEquals(searchInput, field.shadowRoot!.activeElement);
 
-    field.shadowRoot.querySelector('#searchInput').blur();
+    field.$.searchInput.blur();
     assertFalse(field.showingSearch);
 
     field.click();
-    assertEquals(searchInput, field.root.activeElement);
+    assertEquals(searchInput, field.shadowRoot!.activeElement);
 
     pressAndReleaseKeyOn(searchInput, 27, '', 'Escape');
     assertFalse(field.showingSearch, 'Pressing escape closes field.');
-    assertNotEquals(searchInput, field.root.activeElement);
+    assertNotEquals(searchInput, field.shadowRoot!.activeElement);
   });
 
   test('clear search button clears and refocuses input', function() {
@@ -80,14 +76,13 @@
     flush();
     assertTrue(field.hasSearchText);
 
-    const clearSearch = field.shadowRoot.querySelector('#clearSearch');
+    const clearSearch =
+        field.shadowRoot!.querySelector<HTMLElement>('#clearSearch')!;
     clearSearch.focus();
     clearSearch.click();
     assertTrue(field.showingSearch);
     assertEquals('', field.getValue());
-    assertEquals(
-        field.shadowRoot.querySelector('#searchInput'),
-        field.root.activeElement);
+    assertEquals(field.$.searchInput, field.shadowRoot!.activeElement);
     assertFalse(field.hasSearchText);
     assertFalse(field.spinnerActive);
   });
@@ -98,7 +93,7 @@
     flush();
     assertEquals('query1', field.getValue());
 
-    field.shadowRoot.querySelector('#clearSearch').click();
+    field.shadowRoot!.querySelector<HTMLElement>('#clearSearch')!.click();
     assertTrue(field.showingSearch);
     assertEquals('', field.getValue());
 
@@ -184,7 +179,7 @@
       counter++;
       // Calling setValue() with the already existing value should not
       // trigger another 'search-changed' event.
-      field.setValue(event.detail);
+      field.setValue((event as CustomEvent<string>).detail);
     });
 
     field.click();
@@ -196,7 +191,7 @@
   test('blur does not close field when a search is active', function() {
     field.click();
     simulateSearch('test');
-    field.shadowRoot.querySelector('#searchInput').blur();
+    field.$.searchInput.blur();
 
     assertTrue(field.showingSearch);
   });
@@ -210,13 +205,14 @@
     assertTrue(field.hasSearchText);
     flush();
 
-    const clearSearch = field.shadowRoot.querySelector('#clearSearch');
+    const clearSearch =
+        field.shadowRoot!.querySelector<HTMLElement>('#clearSearch')!;
     assertFalse(clearSearch.hidden);
     assertTrue(field.showingSearch);
   });
 
   test('closes when value is cleared while unfocused', function() {
-    field.shadowRoot.querySelector('#searchInput').focus();
+    field.$.searchInput.focus();
     simulateSearch('test');
     flush();
 
@@ -227,7 +223,7 @@
 
     // Does close the field if it is blurred before being cleared.
     simulateSearch('test');
-    field.shadowRoot.querySelector('#searchInput').blur();
+    field.$.searchInput.blur();
     field.setValue('');
     assertFalse(field.showingSearch);
   });
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_test.js b/chrome/test/data/webui/cr_elements/cr_toolbar_test.js
deleted file mode 100644
index 264dd24..0000000
--- a/chrome/test/data/webui/cr_elements/cr_toolbar_test.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2021 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/** @fileoverview Suite of tests for cr-toolbar. */
-
-// clang-format off
-import {CrToolbarElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
-import {assertFalse, assertTrue} from '../chai_assert.js';
-import {isVisible} from '../test_util.js';
-// clang-format on
-
-suite('cr-toolbar', function() {
-  /** @type {?CrToolbarElement} */
-  let toolbar = null;
-
-  setup(function() {
-    document.documentElement.toggleAttribute('enable-branding-update', true);
-    document.body.innerHTML = '';
-    toolbar =
-        /** @type {!CrToolbarElement} */ (document.createElement('cr-toolbar'));
-    document.body.appendChild(toolbar);
-  });
-
-  test('AlwaysShowLogo', function() {
-    toolbar.narrow = true;
-    assertFalse(isVisible(/** @type {!HTMLElement} */ (
-        toolbar.shadowRoot.querySelector('picture'))));
-
-    toolbar.alwaysShowLogo = true;
-    assertTrue(isVisible(/** @type {!HTMLElement} */ (
-        toolbar.shadowRoot.querySelector('picture'))));
-  });
-});
diff --git a/chrome/test/data/webui/cr_elements/cr_toolbar_test.ts b/chrome/test/data/webui/cr_elements/cr_toolbar_test.ts
new file mode 100644
index 0000000..3c753c45
--- /dev/null
+++ b/chrome/test/data/webui/cr_elements/cr_toolbar_test.ts
@@ -0,0 +1,32 @@
+// 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 Suite of tests for cr-toolbar. */
+
+// clang-format off
+import 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
+
+import {CrToolbarElement} from 'chrome://resources/cr_elements/cr_toolbar/cr_toolbar.js';
+import {assertFalse, assertTrue} from 'chrome://webui-test/chai_assert.js';
+import {isVisible} from 'chrome://webui-test/test_util.js';
+// clang-format on
+
+suite('cr-toolbar', function() {
+  let toolbar: CrToolbarElement;
+
+  setup(function() {
+    document.documentElement.toggleAttribute('enable-branding-update', true);
+    document.body.innerHTML = '';
+    toolbar = document.createElement('cr-toolbar');
+    document.body.appendChild(toolbar);
+  });
+
+  test('AlwaysShowLogo', function() {
+    toolbar.narrow = true;
+    assertFalse(isVisible(toolbar.shadowRoot!.querySelector('picture')));
+
+    toolbar.alwaysShowLogo = true;
+    assertTrue(isVisible(toolbar.shadowRoot!.querySelector('picture')));
+  });
+});
diff --git a/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js b/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js
index 0afd085..91af902b 100644
--- a/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js
+++ b/chrome/test/data/webui/settings/chromeos/os_bluetooth_device_detail_subpage_tests.js
@@ -74,6 +74,8 @@
       async function() {
         init();
         bluetoothConfig.setBluetoothEnabledState(/*enabled=*/ true);
+        const windowPopstatePromise =
+            test_util.eventToPromise('popstate', window);
 
         const getBluetoothConnectDisconnectBtn = () =>
             bluetoothDeviceDetailPage.$$('#connectDisconnectBtn');
@@ -113,7 +115,7 @@
         assertTrue(!!getConnectionFailedText());
 
         settings.Router.getInstance().navigateToPreviousRoute();
-        await test_util.waitAfterNextRender(bluetoothDeviceDetailPage);
+        await windowPopstatePromise;
         assertFalse(!!getConnectionFailedText());
       });
 
@@ -283,8 +285,8 @@
 
     assertTrue(!!getBluetoothStatusIcon());
     assertTrue(!!getBluetoothStateText());
-    assertTrue(!!getBluetoothForgetBtn());
     assertTrue(!!getBluetoothDeviceNameLabel());
+    assertFalse(!!getBluetoothForgetBtn());
     assertFalse(!!getBluetoothStateBtn());
     assertFalse(!!getBluetoothDeviceBatteryInfo());
 
@@ -311,6 +313,8 @@
         settings.routes.BLUETOOTH_DEVICE_DETAIL, params);
     await flushAsync();
 
+    assertTrue(!!getBluetoothForgetBtn());
+
     // Simulate connected state and audio capable.
     assertTrue(!!getBluetoothStateBtn());
     assertEquals(
diff --git a/chrome/test/data/webui/settings/cr_settings_browsertest.js b/chrome/test/data/webui/settings/cr_settings_browsertest.js
index 27b56ed..e62e0ccf 100644
--- a/chrome/test/data/webui/settings/cr_settings_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_browsertest.js
@@ -485,9 +485,17 @@
   }
 };
 
-TEST_F('CrSettingsPrivacyReviewPageTest', 'PrivacyReviewPageTests', function() {
-  runMochaSuite('PrivacyReviewPage');
-});
+// TODO(crbug.com/1281967): Flaky on debug Linux builds.
+GEN('#if defined(OS_LINUX) && !defined(NDEBUG)');
+GEN('#define MAYBE_PrivacyReviewPageTests DISABLED_PrivacyReviewPageTests');
+GEN('#else');
+GEN('#define MAYBE_PrivacyReviewPageTests PrivacyReviewPageTests');
+GEN('#endif');
+TEST_F(
+    'CrSettingsPrivacyReviewPageTest', 'MAYBE_PrivacyReviewPageTests',
+    function() {
+      runMochaSuite('PrivacyReviewPage');
+    });
 
 
 TEST_F(
diff --git a/chromeos/components/test/data/onc/policy/policy_cellular_with_no_smdp.onc b/chromeos/components/test/data/onc/policy/policy_cellular_with_no_smdp.onc
new file mode 100644
index 0000000..8b1ffa2
--- /dev/null
+++ b/chromeos/components/test/data/onc/policy/policy_cellular_with_no_smdp.onc
@@ -0,0 +1,10 @@
+{
+  "NetworkConfigurations": [
+    {
+      "GUID": "policy_cellular3",
+      "Type": "Cellular",
+      "Name": "Managed cellular3",
+    }
+  ],
+  "Type": "UnencryptedConfiguration"
+}
diff --git a/chromeos/crosapi/cpp/BUILD.gn b/chromeos/crosapi/cpp/BUILD.gn
index 7cb2a9d..d612428 100644
--- a/chromeos/crosapi/cpp/BUILD.gn
+++ b/chromeos/crosapi/cpp/BUILD.gn
@@ -2,6 +2,8 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
+import("//build/config/chromeos/ui_mode.gni")
+
 # C++ components used by both lacros-chrome and ash-chrome.
 config("crosapi_implementation") {
   defines = [ "IS_CROSAPI_IMPL" ]
@@ -25,6 +27,13 @@
     "//chromeos/crosapi/mojom",
     "//mojo/public/cpp/bindings",
   ]
+
+  if (is_chromeos_ash) {
+    sources += [
+      "lacros_startup_state.cc",
+      "lacros_startup_state.h",
+    ]
+  }
 }
 
 source_set("unit_tests") {
diff --git a/chromeos/crosapi/cpp/lacros_startup_state.cc b/chromeos/crosapi/cpp/lacros_startup_state.cc
new file mode 100644
index 0000000..3291d5d
--- /dev/null
+++ b/chromeos/crosapi/cpp/lacros_startup_state.cc
@@ -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.
+
+#include "chromeos/crosapi/cpp/lacros_startup_state.h"
+
+namespace {
+
+bool g_is_lacros_enabled = false;
+bool g_is_lacros_primary_enabled = false;
+
+}  //  namespace
+
+namespace crosapi {
+
+namespace lacros_startup_state {
+
+void SetLacrosStartupState(bool is_enabled, bool is_primary_enabled) {
+  g_is_lacros_enabled = is_enabled;
+  g_is_lacros_primary_enabled = is_primary_enabled;
+}
+
+bool IsLacrosEnabled() {
+  return g_is_lacros_enabled;
+}
+
+bool IsLacrosPrimaryEnabled() {
+  return g_is_lacros_primary_enabled;
+}
+
+}  // namespace lacros_startup_state
+
+}  //  namespace crosapi
diff --git a/chromeos/crosapi/cpp/lacros_startup_state.h b/chromeos/crosapi/cpp/lacros_startup_state.h
new file mode 100644
index 0000000..5a166a6
--- /dev/null
+++ b/chromeos/crosapi/cpp/lacros_startup_state.h
@@ -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.
+
+#ifndef CHROMEOS_CROSAPI_CPP_LACROS_STARTUP_STATE_H_
+#define CHROMEOS_CROSAPI_CPP_LACROS_STARTUP_STATE_H_
+
+#include "base/component_export.h"
+
+namespace crosapi {
+
+namespace lacros_startup_state {
+
+// Transfers the Lacros startup state from the browser level to lower levels
+// like components.
+COMPONENT_EXPORT(CROSAPI)
+void SetLacrosStartupState(bool is_enabled, bool is_primary_enabled);
+
+// Mirroring the Lacros enabled flag for components and other lower than browser
+// components for dependent feature development.
+COMPONENT_EXPORT(CROSAPI) bool IsLacrosEnabled();
+
+// Mirroring the Lacros Primary enabled flag for components and other lower than
+// browser components for dependent feature development.
+COMPONENT_EXPORT(CROSAPI) bool IsLacrosPrimaryEnabled();
+
+}  // namespace lacros_startup_state
+
+}  // namespace crosapi
+
+#endif  //  CHROMEOS_CROSAPI_CPP_LACROS_STARTUP_STATE_H_
diff --git a/chromeos/crosapi/mojom/app_service_types.mojom b/chromeos/crosapi/mojom/app_service_types.mojom
index 62f70dfa..9080345 100644
--- a/chromeos/crosapi/mojom/app_service_types.mojom
+++ b/chromeos/crosapi/mojom/app_service_types.mojom
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Next MinVersion: 15
+// Next MinVersion: 16
 
 module crosapi.mojom;
 
@@ -309,7 +309,10 @@
 [Stable]
 struct IntentFile {
   // The file path of the file to share.
-  mojo_base.mojom.FilePath file_path;
+  mojo_base.mojom.FilePath file_path@0;
+
+  // File MIME type.
+  [MinVersion=15] string? mime_type@1;
 };
 
 // Action and resource handling request. This should
diff --git a/chromeos/network/BUILD.gn b/chromeos/network/BUILD.gn
index 9d280c8d..ceec44c 100644
--- a/chromeos/network/BUILD.gn
+++ b/chromeos/network/BUILD.gn
@@ -83,12 +83,16 @@
     "managed_network_configuration_handler_impl.h",
     "managed_state.cc",
     "managed_state.h",
+    "metrics/connection_info_metrics_logger.cc",
+    "metrics/connection_info_metrics_logger.h",
     "metrics/esim_policy_login_metrics_logger.cc",
     "metrics/esim_policy_login_metrics_logger.h",
     "metrics/network_metrics_helper.cc",
     "metrics/network_metrics_helper.h",
     "metrics/shill_connect_result.cc",
     "metrics/shill_connect_result.h",
+    "metrics/vpn_network_metrics_helper.cc",
+    "metrics/vpn_network_metrics_helper.h",
     "network_activation_handler.h",
     "network_activation_handler_impl.cc",
     "network_activation_handler_impl.h",
@@ -297,8 +301,10 @@
     "firewall_hole_unittest.cc",
     "geolocation_handler_unittest.cc",
     "managed_network_configuration_handler_unittest.cc",
+    "metrics/connection_info_metrics_logger_unittest.cc",
     "metrics/esim_policy_login_metrics_logger_unittest.cc",
     "metrics/network_metrics_helper_unittest.cc",
+    "metrics/vpn_network_metrics_helper_unittest.cc",
     "network_cert_loader_unittest.cc",
     "network_cert_migrator_unittest.cc",
     "network_configuration_handler_unittest.cc",
diff --git a/chromeos/network/managed_network_configuration_handler_unittest.cc b/chromeos/network/managed_network_configuration_handler_unittest.cc
index e7e1aea..05a318e 100644
--- a/chromeos/network/managed_network_configuration_handler_unittest.cc
+++ b/chromeos/network/managed_network_configuration_handler_unittest.cc
@@ -59,29 +59,34 @@
 constexpr char kUser1[] = "user1";
 constexpr char kUser1ProfilePath[] = "/profile/user1/shill";
 
-// The GUID used by chromeos/test/data/network/policy/*.{json,onc} files for a
-// VPN.
+// The GUID used by chromeos/components/test/data/onc/policy/*.{json,onc} files
+// for a VPN.
 constexpr char kTestGuidVpn[] = "{a3860e83-f03d-4cb1-bafa-b22c9e746950}";
 
-// The GUID used by chromeos/test/data/network/policy/*.{json,onc} files for a
-// managed Wifi service.
+// The GUID used by chromeos/components/test/data/onc/policy/*.{json,onc} files
+// for a managed Wifi service.
 constexpr char kTestGuidManagedWifi[] = "policy_wifi1";
 
-// The GUID used by chromeos/test/data/network/policy/policy_cellular.onc files
-// for a managed Cellular service.
+// The GUID used by chromeos/components/test/data/onc/policy/policy_cellular.onc
+// files for a managed Cellular service.
 constexpr char kTestGuidManagedCellular[] = "policy_cellular";
 
 // The GUID used by
-// chromeos/test/data/network/policy/policy_cellular_with_iccid.onc files for a
-// managed Cellular service.
+// chromeos/components/test/data/onc/policy/policy_cellular_with_iccid.onc files
+// for a managed Cellular service.
 constexpr char kTestGuidManagedCellular2[] = "policy_cellular2";
 
-// The GUID used by chromeos/test/data/network/policy/*.{json,onc} files for an
-// unmanaged Wifi service.
+// The GUID used by
+// chromeos/components/test/data/onc/policy/policy_cellular_with_no_smdp.onc
+// files for a managed Cellular service.
+constexpr char kTestGuidManagedCellular3[] = "policy_cellular3";
+
+// The GUID used by chromeos/components/test/data/onc/policy/*.{json,onc} files
+// for an unmanaged Wifi service.
 constexpr char kTestGuidUnmanagedWifi2[] = "wifi2";
 
-// The GUID used by chromeos/test/data/network/policy/*.{json,onc} files for a
-// Wifi service.
+// The GUID used by chromeos/components/test/data/onc/policy/*.{json,onc} files
+// for a Wifi service.
 constexpr char kTestGuidEthernetEap[] = "policy_ethernet_eap";
 
 constexpr char kTestEuiccPath[] = "/org/chromium/Hermes/Euicc/0";
@@ -423,6 +428,40 @@
   ASSERT_TRUE(*auto_connect);
 }
 
+TEST_F(ManagedNetworkConfigurationHandlerTest,
+       SetPolicyManagedCellularDisableFeatureFlag) {
+  base::test::ScopedFeatureList feature_list;
+  InitializeStandardProfiles();
+  InitializeEuicc();
+  // Verify that when eSIM policy feature flag is not set, applying managed
+  // eSIM policy should not create a new shill configuration for it.
+  feature_list.InitAndDisableFeature(ash::features::kESimPolicy);
+  SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+            "policy/policy_cellular.onc");
+  FastForwardProfileRefreshDelay();
+  base::RunLoop().RunUntilIdle();
+  std::string service_path = GetShillServiceClient()->FindServiceMatchingGUID(
+      kTestGuidManagedCellular);
+  ASSERT_EQ(service_path, std::string());
+}
+
+TEST_F(ManagedNetworkConfigurationHandlerTest,
+       SetPolicyIgnoreNoSmdpManagedCellular) {
+  base::test::ScopedFeatureList feature_list;
+  InitializeStandardProfiles();
+  InitializeEuicc();
+  // Verify that applying managed eSIM policy with no SMDP address in the ONC
+  // should not create a new shill configuration for it.
+  feature_list.InitAndEnableFeature(ash::features::kESimPolicy);
+  SetPolicy(::onc::ONC_SOURCE_DEVICE_POLICY, std::string(),
+            "policy/policy_cellular_with_no_smdp.onc");
+  FastForwardProfileRefreshDelay();
+  base::RunLoop().RunUntilIdle();
+  std::string service_path = GetShillServiceClient()->FindServiceMatchingGUID(
+      kTestGuidManagedCellular3);
+  ASSERT_EQ(service_path, std::string());
+}
+
 TEST_F(ManagedNetworkConfigurationHandlerTest, SetPolicyManageUnconfigured) {
   InitializeStandardProfiles();
   std::unique_ptr<base::DictionaryValue> expected_shill_properties =
diff --git a/chromeos/network/metrics/connection_info_metrics_logger.cc b/chromeos/network/metrics/connection_info_metrics_logger.cc
new file mode 100644
index 0000000..b5a925c0
--- /dev/null
+++ b/chromeos/network/metrics/connection_info_metrics_logger.cc
@@ -0,0 +1,145 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/metrics/connection_info_metrics_logger.h"
+
+#include "base/containers/flat_set.h"
+#include "chromeos/network/metrics/network_metrics_helper.h"
+#include "chromeos/network/network_connection_handler.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+
+namespace chromeos {
+
+ConnectionInfoMetricsLogger::ConnectionInfo::ConnectionInfo(
+    const NetworkState* network,
+    bool was_disconnect_requested)
+    : guid(network->guid()),
+      shill_error(network->GetError()),
+      was_disconnect_requested(was_disconnect_requested) {
+  if (network->IsConnectedState())
+    status = Status::kConnected;
+  else if (network->IsConnectingState())
+    status = Status::kConnecting;
+  else
+    status = Status::kDisconnected;
+}
+
+ConnectionInfoMetricsLogger::ConnectionInfo::~ConnectionInfo() = default;
+
+bool ConnectionInfoMetricsLogger::ConnectionInfo::operator==(
+    const ConnectionInfoMetricsLogger::ConnectionInfo& other) const {
+  return status == other.status &&
+         was_disconnect_requested == other.was_disconnect_requested &&
+         guid == other.guid && shill_error == other.shill_error;
+}
+
+ConnectionInfoMetricsLogger::ConnectionInfoMetricsLogger() = default;
+
+ConnectionInfoMetricsLogger::~ConnectionInfoMetricsLogger() {
+  if (network_state_handler_)
+    network_state_handler_->RemoveObserver(this, FROM_HERE);
+  if (network_connection_handler_)
+    network_connection_handler_->RemoveObserver(this);
+}
+
+void ConnectionInfoMetricsLogger::Init(
+    NetworkStateHandler* network_state_handler,
+    NetworkConnectionHandler* network_connection_handler) {
+  if (network_connection_handler) {
+    network_connection_handler_ = network_connection_handler;
+    network_connection_handler_->AddObserver(this);
+  }
+
+  if (network_state_handler) {
+    network_state_handler_ = network_state_handler;
+    network_state_handler_->AddObserver(this, FROM_HERE);
+    NetworkListChanged();
+  }
+}
+
+void ConnectionInfoMetricsLogger::NetworkListChanged() {
+  NetworkStateHandler::NetworkStateList network_list;
+  network_state_handler_->GetVisibleNetworkList(&network_list);
+
+  base::flat_set<std::string> visible_guids;
+  for (const auto* network : network_list) {
+    UpdateConnectionInfo(network);
+    visible_guids.insert(network->guid());
+  }
+
+  // Remove networks that are no longer visible.
+  for (const auto& connection_info : guid_to_connection_info_) {
+    const std::string guid = connection_info.first;
+    if (visible_guids.find(guid) == visible_guids.end())
+      guid_to_connection_info_.erase(guid);
+  }
+}
+
+void ConnectionInfoMetricsLogger::NetworkConnectionStateChanged(
+    const NetworkState* network) {
+  UpdateConnectionInfo(network);
+}
+
+void ConnectionInfoMetricsLogger::DisconnectRequested(
+    const std::string& service_path) {
+  if (!network_state_handler_)
+    return;
+
+  const NetworkState* network =
+      network_state_handler_->GetNetworkState(service_path);
+
+  if (!network)
+    return;
+
+  UpdateConnectionInfo(network, /*disconnect_requested=*/true);
+}
+
+void ConnectionInfoMetricsLogger::UpdateConnectionInfo(
+    const NetworkState* network,
+    bool disconnect_requested) {
+  const absl::optional<ConnectionInfo> prev_info =
+      GetCachedInfo(network->guid());
+  ConnectionInfo curr_info =
+      ConnectionInfo(network,
+                     /*was_disconnect_requested=*/disconnect_requested);
+
+  // No updates if the ConnectionInfo did not change.
+  if (prev_info == curr_info)
+    return;
+
+  // If a disconnect has been requested, maintain it until the status changes.
+  if (prev_info && prev_info->status == curr_info.status)
+    curr_info.was_disconnect_requested |= prev_info->was_disconnect_requested;
+  else
+    AttemptLogAllConnectionResult(prev_info, curr_info);
+
+  guid_to_connection_info_.insert_or_assign(network->guid(), curr_info);
+}
+
+void ConnectionInfoMetricsLogger::AttemptLogAllConnectionResult(
+    const absl::optional<ConnectionInfo>& prev_info,
+    const ConnectionInfo& curr_info) const {
+  DCHECK(!prev_info || prev_info && prev_info->guid == curr_info.guid);
+
+  if (curr_info.status == ConnectionInfo::Status::kConnected)
+    NetworkMetricsHelper::LogAllConnectionResult(curr_info.guid);
+
+  if (prev_info && !prev_info->was_disconnect_requested &&
+      prev_info->status == ConnectionInfo::Status::kConnecting &&
+      curr_info.status == ConnectionInfo::Status::kDisconnected) {
+    NetworkMetricsHelper::LogAllConnectionResult(curr_info.guid,
+                                                 curr_info.shill_error);
+  }
+}
+
+absl::optional<ConnectionInfoMetricsLogger::ConnectionInfo>
+ConnectionInfoMetricsLogger::GetCachedInfo(const std::string& guid) const {
+  const auto prev_info_it = guid_to_connection_info_.find(guid);
+  if (prev_info_it == guid_to_connection_info_.end())
+    return absl::nullopt;
+  return prev_info_it->second;
+}
+
+}  // namespace chromeos
\ No newline at end of file
diff --git a/chromeos/network/metrics/connection_info_metrics_logger.h b/chromeos/network/metrics/connection_info_metrics_logger.h
new file mode 100644
index 0000000..ff8c489
--- /dev/null
+++ b/chromeos/network/metrics/connection_info_metrics_logger.h
@@ -0,0 +1,93 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_NETWORK_METRICS_CONNECTION_INFO_METRICS_LOGGER_H_
+#define CHROMEOS_NETWORK_METRICS_CONNECTION_INFO_METRICS_LOGGER_H_
+
+#include "base/component_export.h"
+#include "base/containers/flat_map.h"
+#include "chromeos/network/network_connection_observer.h"
+#include "chromeos/network/network_state_handler_observer.h"
+#include "third_party/abseil-cpp/absl/types/optional.h"
+
+namespace chromeos {
+
+class NetworkState;
+class NetworkConnectionHandler;
+class NetworkStateHandler;
+
+// Class for tracking general connection information about networks.
+//
+// This class adds observers on network state and makes the following
+// measurements on all networks:
+// 1. Success rate of all connection attempts.
+// TODO(b/207589664): MORE TBA
+//
+// Note: This class does not start logging metrics until Init() is
+// invoked.
+class COMPONENT_EXPORT(CHROMEOS_NETWORK) ConnectionInfoMetricsLogger
+    : public NetworkStateHandlerObserver,
+      public NetworkConnectionObserver {
+ public:
+  ConnectionInfoMetricsLogger();
+  ConnectionInfoMetricsLogger(const ConnectionInfoMetricsLogger&) = delete;
+  ConnectionInfoMetricsLogger& operator=(const ConnectionInfoMetricsLogger&) =
+      delete;
+  ~ConnectionInfoMetricsLogger() override;
+
+  void Init(NetworkStateHandler* network_state_handler,
+            NetworkConnectionHandler* network_connection_handler);
+
+ private:
+  friend class ConnectionInfoMetricsLoggerTest;
+
+  // Stores connection related information for a network.
+  struct ConnectionInfo {
+   public:
+    enum class Status {
+      // The network is not connected or being connected to.
+      kDisconnected = 0,
+
+      // The network is being connected to.
+      kConnecting = 1,
+
+      // The network is connected.
+      kConnected = 2,
+    };
+
+    ConnectionInfo(const NetworkState* network, bool was_disconnect_requested);
+    ~ConnectionInfo();
+
+    bool operator==(const ConnectionInfo& other) const;
+
+    Status status;
+    std::string guid;
+    std::string shill_error;
+    bool was_disconnect_requested;
+  };
+
+  // NetworkStateHandlerObserver::
+  void NetworkListChanged() override;
+  void NetworkConnectionStateChanged(const NetworkState* network) override;
+
+  // NetworkConnectionObserver::
+  void DisconnectRequested(const std::string& service_path) override;
+
+  void UpdateConnectionInfo(const NetworkState* network,
+                            bool disconnect_requested = false);
+  void AttemptLogAllConnectionResult(
+      const absl::optional<ConnectionInfo>& prev_info,
+      const ConnectionInfo& curr_info) const;
+  absl::optional<ConnectionInfo> GetCachedInfo(const std::string& guid) const;
+
+  NetworkStateHandler* network_state_handler_;
+  NetworkConnectionHandler* network_connection_handler_;
+
+  // Stores connection information for all networks.
+  base::flat_map<std::string, ConnectionInfo> guid_to_connection_info_;
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_NETWORK_METRICS_CONNECTION_INFO_METRICS_LOGGER_H_
diff --git a/chromeos/network/metrics/connection_info_metrics_logger_unittest.cc b/chromeos/network/metrics/connection_info_metrics_logger_unittest.cc
new file mode 100644
index 0000000..c5b1f791
--- /dev/null
+++ b/chromeos/network/metrics/connection_info_metrics_logger_unittest.cc
@@ -0,0 +1,198 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/network/metrics/connection_info_metrics_logger.h"
+
+#include <memory>
+
+#include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "chromeos/dbus/shill/shill_service_client.h"
+#include "chromeos/network/metrics/shill_connect_result.h"
+#include "chromeos/network/network_handler_test_helper.h"
+#include "chromeos/network/network_state_handler.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+// Note: The actual network types used does not matter as networks are kept
+// track of by GUID. This test uses Cellular and Wifi, but any combination of
+// network type may be used.
+const char kCellularConnectResultAllHistogram[] =
+    "Network.Ash.Cellular.ConnectionResult.All";
+const char kWifiConnectResultAllHistogram[] =
+    "Network.Ash.WiFi.ConnectionResult.All";
+
+const char kCellularGuid[] = "test_guid";
+const char kCellularServicePath[] = "/service/network";
+const char kCellularName[] = "network_name";
+
+const char kWifiGuid[] = "test_guid2";
+const char kWifiServicePath[] = "/service/network2";
+const char kWifiName[] = "network_name2";
+
+}  // namespace
+
+class ConnectionInfoMetricsLoggerTest : public testing::Test {
+ public:
+  ConnectionInfoMetricsLoggerTest() = default;
+  ConnectionInfoMetricsLoggerTest(const ConnectionInfoMetricsLoggerTest&) =
+      delete;
+  ConnectionInfoMetricsLoggerTest& operator=(
+      const ConnectionInfoMetricsLoggerTest&) = delete;
+  ~ConnectionInfoMetricsLoggerTest() override = default;
+
+  void SetUp() override {
+    network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
+
+    shill_service_client_ = ShillServiceClient::Get()->GetTestInterface();
+    shill_service_client_->ClearServices();
+    base::RunLoop().RunUntilIdle();
+
+    network_handler_test_helper_->RegisterPrefs(profile_prefs_.registry(),
+                                                local_state_.registry());
+
+    network_handler_test_helper_->InitializePrefs(&profile_prefs_,
+                                                  &local_state_);
+  }
+
+  void TearDown() override {
+    shill_service_client_->ClearServices();
+    network_handler_test_helper_.reset();
+  }
+
+  void SetUpGenericCellularNetwork() {
+    shill_service_client_->AddService(kCellularServicePath, kCellularGuid,
+                                      kCellularName, shill::kTypeCellular,
+                                      shill::kStateIdle,
+                                      /*visible=*/true);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetUpGenericWifiNetwork() {
+    shill_service_client_->AddService(kWifiServicePath, kWifiGuid, kWifiName,
+                                      shill::kTypeWifi, shill::kStateIdle,
+                                      /*visible=*/true);
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void SetShillState(const std::string& service_path,
+                     const std::string& shill_state) {
+    shill_service_client_->SetServiceProperty(
+        service_path, shill::kStateProperty, base::Value(shill_state));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void RequestDisconnect(const std::string& service_path) {
+    chromeos::NetworkHandler::Get()
+        ->connection_info_metrics_logger_->DisconnectRequested(service_path);
+  }
+
+ protected:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+  std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
+  ShillServiceClient::TestInterface* shill_service_client_;
+  TestingPrefServiceSimple profile_prefs_;
+  TestingPrefServiceSimple local_state_;
+};
+
+TEST_F(ConnectionInfoMetricsLoggerTest, AutoStatusTransitions) {
+  SetUpGenericCellularNetwork();
+
+  // Successful connect from disconnected to connected.
+  SetShillState(kCellularServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 0);
+  SetShillState(kCellularServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  histogram_tester_->ExpectBucketCount(kCellularConnectResultAllHistogram,
+                                       ShillConnectResult::kSuccess, 1);
+
+  // Successful connect from connecting to connected.
+  SetShillState(kCellularServicePath, shill::kStateAssociation);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  SetShillState(kCellularServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 2);
+  histogram_tester_->ExpectBucketCount(kCellularConnectResultAllHistogram,
+                                       ShillConnectResult::kSuccess, 2);
+
+  // Fail to connect from connecting to disconnected without disconnection
+  // request.
+  SetShillState(kCellularServicePath, shill::kStateAssociation);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 2);
+  SetShillState(kCellularServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 3);
+  histogram_tester_->ExpectBucketCount(kCellularConnectResultAllHistogram,
+                                       ShillConnectResult::kUnknown, 1);
+}
+
+TEST_F(ConnectionInfoMetricsLoggerTest, MultipleNetworksStatusRecorded) {
+  SetUpGenericCellularNetwork();
+  SetUpGenericWifiNetwork();
+
+  SetShillState(kCellularServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 0);
+  histogram_tester_->ExpectTotalCount(kWifiConnectResultAllHistogram, 0);
+
+  SetShillState(kCellularServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  histogram_tester_->ExpectTotalCount(kWifiConnectResultAllHistogram, 0);
+  histogram_tester_->ExpectBucketCount(kCellularConnectResultAllHistogram,
+                                       ShillConnectResult::kSuccess, 1);
+
+  SetShillState(kWifiServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  histogram_tester_->ExpectTotalCount(kWifiConnectResultAllHistogram, 1);
+  histogram_tester_->ExpectBucketCount(kWifiConnectResultAllHistogram,
+                                       ShillConnectResult::kSuccess, 1);
+
+  SetShillState(kWifiServicePath, shill::kStateAssociation);
+  SetShillState(kWifiServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  histogram_tester_->ExpectTotalCount(kWifiConnectResultAllHistogram, 2);
+  histogram_tester_->ExpectBucketCount(kWifiConnectResultAllHistogram,
+                                       ShillConnectResult::kSuccess, 1);
+}
+
+TEST_F(ConnectionInfoMetricsLoggerTest, DisconnectRequested) {
+  SetUpGenericCellularNetwork();
+
+  // Disconnect Requested while connected.
+  SetShillState(kCellularServicePath, shill::kStateOnline);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  RequestDisconnect(kCellularServicePath);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  SetShillState(kCellularServicePath, shill::kStateReady);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+
+  // Disconnect Requested while disconnected.
+  SetShillState(kCellularServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  RequestDisconnect(kCellularServicePath);
+  SetShillState(kCellularServicePath, shill::kStateDisconnect);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+
+  // Disconnect Requested while connecting.
+  SetShillState(kCellularServicePath, shill::kStateAssociation);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  RequestDisconnect(kCellularServicePath);
+  SetShillState(kCellularServicePath, shill::kStateConfiguration);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+
+  // Cancel connect attempt while connecting state.
+  SetShillState(kCellularServicePath, shill::kStateAssociation);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+  SetShillState(kCellularServicePath, shill::kStateAssociation);
+  RequestDisconnect(kCellularServicePath);
+  SetShillState(kCellularServicePath, shill::kStateIdle);
+  histogram_tester_->ExpectTotalCount(kCellularConnectResultAllHistogram, 1);
+}
+
+}  // namespace chromeos
\ No newline at end of file
diff --git a/chromeos/network/metrics/network_metrics_helper.cc b/chromeos/network/metrics/network_metrics_helper.cc
index 8d8198bf..caa9de7 100644
--- a/chromeos/network/metrics/network_metrics_helper.cc
+++ b/chromeos/network/metrics/network_metrics_helper.cc
@@ -16,13 +16,15 @@
 
 namespace {
 
-const char kNetworkMetricsPrefix[] = "Network.";
+const char kNetworkMetricsPrefix[] = "Network.Ash.";
 const char kAllConnectionResultSuffix[] = ".ConnectionResult.All";
 
 const char kWifi[] = "WiFi";
 const char kWifiOpen[] = "WiFi.SecurityOpen";
 const char kWifiPasswordProtected[] = "WiFi.SecurityPasswordProtected";
 
+const char kTether[] = "Tether";
+
 chromeos::NetworkStateHandler* GetNetworkStateHandler() {
   return NetworkHandler::Get()->network_state_handler();
 }
@@ -75,8 +77,7 @@
 
 const std::vector<std::string> GetTetherNetworkTypeHistograms(
     const NetworkState* network_state) {
-  // TODO(b/207589664): Determine histogram variant names for Tether.
-  return {};
+  return {kTether};
 }
 
 const std::vector<std::string> GetVpnNetworkTypeHistograms(
diff --git a/chromeos/network/metrics/network_metrics_helper.h b/chromeos/network/metrics/network_metrics_helper.h
index 8f7c7af..8fc7b92 100644
--- a/chromeos/network/metrics/network_metrics_helper.h
+++ b/chromeos/network/metrics/network_metrics_helper.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// 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/chromeos/network/metrics/network_metrics_helper_unittest.cc b/chromeos/network/metrics/network_metrics_helper_unittest.cc
index 23fb4df8..9fef3ff 100644
--- a/chromeos/network/metrics/network_metrics_helper_unittest.cc
+++ b/chromeos/network/metrics/network_metrics_helper_unittest.cc
@@ -24,34 +24,35 @@
 
 // LogAllConnectionResult() Cellular histograms.
 const char kCellularConnectResultAllHistogram[] =
-    "Network.Cellular.ConnectionResult.All";
+    "Network.Ash.Cellular.ConnectionResult.All";
 const char kCellularESimConnectResultAllHistogram[] =
-    "Network.Cellular.ESim.ConnectionResult.All";
+    "Network.Ash.Cellular.ESim.ConnectionResult.All";
 const char kCellularPSimConnectResultAllHistogram[] =
-    "Network.Cellular.PSim.ConnectionResult.All";
+    "Network.Ash.Cellular.PSim.ConnectionResult.All";
 
 // LogAllConnectionResult() VPN histograms.
-const char kVpnConnectResultAllHistogram[] = "Network.VPN.ConnectionResult.All";
+const char kVpnConnectResultAllHistogram[] =
+    "Network.Ash.VPN.ConnectionResult.All";
 const char kVpnBuiltInConnectResultAllHistogram[] =
-    "Network.VPN.TypeBuiltIn.ConnectionResult.All";
+    "Network.Ash.VPN.TypeBuiltIn.ConnectionResult.All";
 const char kVpnThirdPartyConnectResultAllHistogram[] =
-    "Network.VPN.TypeThirdParty.ConnectionResult.All";
+    "Network.Ash.VPN.TypeThirdParty.ConnectionResult.All";
 
 // LogAllConnectionResult() WiFi histograms.
 const char kWifiConnectResultAllHistogram[] =
-    "Network.WiFi.ConnectionResult.All";
+    "Network.Ash.WiFi.ConnectionResult.All";
 const char kWifiOpenConnectResultAllHistogram[] =
-    "Network.WiFi.SecurityOpen.ConnectionResult.All";
+    "Network.Ash.WiFi.SecurityOpen.ConnectionResult.All";
 const char kWifiPasswordProtectedConnectResultAllHistogram[] =
-    "Network.WiFi.SecurityPasswordProtected.ConnectionResult.All";
+    "Network.Ash.WiFi.SecurityPasswordProtected.ConnectionResult.All";
 
 // LogAllConnectionResult() Ethernet histograms.
 const char kEthernetConnectResultAllHistogram[] =
-    "Network.Ethernet.ConnectionResult.All";
+    "Network.Ash.Ethernet.ConnectionResult.All";
 const char kEthernetEapConnectResultAllHistogram[] =
-    "Network.Ethernet.Eap.ConnectionResult.All";
+    "Network.Ash.Ethernet.Eap.ConnectionResult.All";
 const char kEthernetNoEapConnectResultAllHistogram[] =
-    "Network.Ethernet.NoEap.ConnectionResult.All";
+    "Network.Ash.Ethernet.NoEap.ConnectionResult.All";
 
 const char kTestGuid[] = "test_guid";
 const char kTestServicePath[] = "/service/network";
@@ -250,9 +251,17 @@
 
   device_test->SetDeviceProperty(kTestDevicePath,
                                  shill::kEapAuthenticationCompletedProperty,
-                                 base::Value(true), /*notify_changed=*/true);
+                                 base::Value(true), /*notify_changed=*/false);
   base::RunLoop().RunUntilIdle();
 
+  // Setting up the Ethernet Eap connection in tests may cause metrics to be
+  // logged automatically by ConnectionInfoMetricsLogger.
+  histogram_tester_.reset(new base::HistogramTester());
+  histogram_tester_->ExpectTotalCount(kEthernetConnectResultAllHistogram, 0);
+  histogram_tester_->ExpectTotalCount(kEthernetEapConnectResultAllHistogram, 0);
+  histogram_tester_->ExpectTotalCount(kEthernetNoEapConnectResultAllHistogram,
+                                      0);
+
   NetworkMetricsHelper::LogAllConnectionResult(kTestGuid,
                                                shill::kErrorNotRegistered);
   histogram_tester_->ExpectTotalCount(kEthernetConnectResultAllHistogram, 1);
diff --git a/chromeos/network/metrics/shill_connect_result.h b/chromeos/network/metrics/shill_connect_result.h
index eb3b4435..2d63ca73 100644
--- a/chromeos/network/metrics/shill_connect_result.h
+++ b/chromeos/network/metrics/shill_connect_result.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2021 The Chromium Authors. All rights reserved.
+// 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/chromeos/network/metrics/vpn_network_metrics_helper.cc b/chromeos/network/metrics/vpn_network_metrics_helper.cc
new file mode 100644
index 0000000..0678bcc
--- /dev/null
+++ b/chromeos/network/metrics/vpn_network_metrics_helper.cc
@@ -0,0 +1,86 @@
+// 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 "chromeos/network/metrics/vpn_network_metrics_helper.h"
+
+#include "base/logging.h"
+#include "base/metrics/histogram_functions.h"
+#include "base/notreached.h"
+#include "chromeos/network/metrics/network_metrics_helper.h"
+#include "chromeos/network/network_handler.h"
+#include "chromeos/network/network_state.h"
+#include "chromeos/network/network_state_handler.h"
+#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
+
+namespace chromeos {
+namespace {
+
+// The buckets of the histogram that captures the metrics of the configuration
+// sources of created VPNs.
+const char kVpnConfigurationSourceBucketArc[] =
+    "Network.Ash.VPN.ARC.ConfigurationSource";
+const char kVpnConfigurationSourceBucketL2tpIpsec[] =
+    "Network.Ash.VPN.L2TPIPsec.ConfigurationSource";
+const char kVpnConfigurationSourceBucketOpenVpn[] =
+    "Network.Ash.VPN.OpenVPN.ConfigurationSource";
+const char kVpnConfigurationSourceBucketThirdParty[] =
+    "Network.Ash.VPN.ThirdParty.ConfigurationSource";
+const char kVpnConfigurationSourceBucketWireGuard[] =
+    "Network.Ash.VPN.WireGuard.ConfigurationSource";
+
+const char* GetBucketForVpnProviderType(const std::string& vpn_provider_type) {
+  if (vpn_provider_type == shill::kProviderArcVpn) {
+    return kVpnConfigurationSourceBucketArc;
+  } else if (vpn_provider_type == shill::kProviderL2tpIpsec) {
+    return kVpnConfigurationSourceBucketL2tpIpsec;
+  } else if (vpn_provider_type == shill::kProviderOpenVpn) {
+    return kVpnConfigurationSourceBucketOpenVpn;
+  } else if (vpn_provider_type == shill::kProviderThirdPartyVpn) {
+    return kVpnConfigurationSourceBucketThirdParty;
+  } else if (vpn_provider_type == shill::kProviderWireGuard) {
+    return kVpnConfigurationSourceBucketWireGuard;
+  }
+  return nullptr;
+}
+
+}  // namespace
+
+VpnNetworkMetricsHelper::VpnNetworkMetricsHelper() = default;
+
+VpnNetworkMetricsHelper::~VpnNetworkMetricsHelper() = default;
+
+void VpnNetworkMetricsHelper::Init(
+    NetworkConfigurationHandler* network_configuration_handler) {
+  if (network_configuration_handler)
+    network_configuration_observation_.Observe(network_configuration_handler);
+}
+
+void VpnNetworkMetricsHelper::OnConfigurationCreated(
+    const std::string& service_path,
+    const std::string& guid) {
+  const NetworkState* network_state = chromeos::NetworkHandler::Get()
+                                          ->network_state_handler()
+                                          ->GetNetworkStateFromGuid(guid);
+
+  if (!network_state || network_state->GetNetworkTechnologyType() !=
+                            NetworkState::NetworkTechnologyType::kVPN) {
+    return;
+  }
+
+  const char* vpn_provider_type_bucket =
+      GetBucketForVpnProviderType(network_state->GetVpnProviderType());
+
+  if (!vpn_provider_type_bucket) {
+    NOTREACHED();
+    return;
+  }
+
+  base::UmaHistogramEnumeration(
+      vpn_provider_type_bucket,
+      network_state->IsManagedByPolicy()
+          ? VPNConfigurationSource::kConfiguredByPolicy
+          : VPNConfigurationSource::kConfiguredManually);
+}
+
+}  // namespace chromeos
diff --git a/chromeos/network/metrics/vpn_network_metrics_helper.h b/chromeos/network/metrics/vpn_network_metrics_helper.h
new file mode 100644
index 0000000..27c448a
--- /dev/null
+++ b/chromeos/network/metrics/vpn_network_metrics_helper.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CHROMEOS_NETWORK_METRICS_VPN_NETWORK_METRICS_HELPER_H_
+#define CHROMEOS_NETWORK_METRICS_VPN_NETWORK_METRICS_HELPER_H_
+
+#include <string>
+
+#include "base/component_export.h"
+#include "base/scoped_observation.h"
+#include "chromeos/network/network_configuration_handler.h"
+#include "chromeos/network/network_configuration_observer.h"
+
+namespace chromeos {
+
+// This class is responsible for tracking the creation of VPN networks,
+// recording the provider type and whether it was configured manually or via
+// policy and reporting this information to the UMA backend.
+class COMPONENT_EXPORT(CHROMEOS_NETWORK) VpnNetworkMetricsHelper
+    : public NetworkConfigurationObserver {
+ public:
+  VpnNetworkMetricsHelper();
+  VpnNetworkMetricsHelper(const VpnNetworkMetricsHelper&) = delete;
+  VpnNetworkMetricsHelper& operator=(const VpnNetworkMetricsHelper&) = delete;
+  ~VpnNetworkMetricsHelper() override;
+
+  void Init(NetworkConfigurationHandler* network_configuration_handler);
+
+ private:
+  friend class VpnNetworkMetricsHelperTest;
+
+  // The different configuration sources for VPN networks. These values are used
+  // when reporting to the UMA backend and should not be renumbered or renamed.
+  // The name "VPNConfigurationSource" is cased to match other metrics.
+  enum class VPNConfigurationSource {
+    kConfiguredManually = 0,
+    kConfiguredByPolicy = 1,
+    kMaxValue = kConfiguredByPolicy,
+  };
+
+  // NetworkConfigurationObserver:
+  void OnConfigurationCreated(const std::string& service_path,
+                              const std::string& guid) override;
+
+  base::ScopedObservation<NetworkConfigurationHandler,
+                          NetworkConfigurationObserver>
+      network_configuration_observation_{this};
+};
+
+}  // namespace chromeos
+
+#endif  // CHROMEOS_NETWORK_METRICS_VPN_NETWORK_METRICS_HELPER_H_
diff --git a/chromeos/network/metrics/vpn_network_metrics_helper_unittest.cc b/chromeos/network/metrics/vpn_network_metrics_helper_unittest.cc
new file mode 100644
index 0000000..57f94f5
--- /dev/null
+++ b/chromeos/network/metrics/vpn_network_metrics_helper_unittest.cc
@@ -0,0 +1,164 @@
+// 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 "chromeos/network/metrics/vpn_network_metrics_helper.h"
+
+#include <memory>
+#include <utility>
+
+#include "base/callback.h"
+#include "base/json/json_writer.h"
+#include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
+#include "base/test/task_environment.h"
+#include "base/values.h"
+#include "chromeos/dbus/shill/shill_service_client.h"
+#include "chromeos/network/network_configuration_handler.h"
+#include "chromeos/network/network_handler_test_helper.h"
+#include "chromeos/network/network_profile_handler.h"
+#include "chromeos/network/network_ui_data.h"
+#include "chromeos/network/shill_property_util.h"
+#include "components/prefs/testing_pref_service.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+const char kVpnHistogramConfigurationSourceArc[] =
+    "Network.Ash.VPN.ARC.ConfigurationSource";
+const char kVpnHistogramConfigurationSourceL2tpIpsec[] =
+    "Network.Ash.VPN.L2TPIPsec.ConfigurationSource";
+const char kVpnHistogramConfigurationSourceOpenVpn[] =
+    "Network.Ash.VPN.OpenVPN.ConfigurationSource";
+const char kVpnHistogramConfigurationSourceThirdParty[] =
+    "Network.Ash.VPN.ThirdParty.ConfigurationSource";
+const char kVpnHistogramConfigurationSourceWireGuard[] =
+    "Network.Ash.VPN.WireGuard.ConfigurationSource";
+
+std::string PrettyJson(const base::DictionaryValue& value) {
+  std::string pretty;
+  base::JSONWriter::WriteWithOptions(
+      value, base::JSONWriter::OPTIONS_PRETTY_PRINT, &pretty);
+  return pretty;
+}
+
+void ErrorCallback(const std::string& error_name,
+                   std::unique_ptr<base::DictionaryValue> error_data) {
+  ADD_FAILURE() << "Unexpected error: " << error_name
+                << " with associated data: \n"
+                << PrettyJson(*error_data);
+}
+
+}  // namespace
+
+class VpnNetworkMetricsHelperTest : public testing::Test {
+ public:
+  VpnNetworkMetricsHelperTest() = default;
+  VpnNetworkMetricsHelperTest(const VpnNetworkMetricsHelperTest&) = delete;
+  VpnNetworkMetricsHelperTest& operator=(const VpnNetworkMetricsHelperTest&) =
+      delete;
+  ~VpnNetworkMetricsHelperTest() override = default;
+
+  void SetUp() override {
+    network_handler_test_helper_ = std::make_unique<NetworkHandlerTestHelper>();
+    histogram_tester_ = std::make_unique<base::HistogramTester>();
+    ClearServices();
+  }
+
+  void TearDown() override {
+    ClearServices();
+    network_handler_test_helper_.reset();
+  }
+
+ protected:
+  void ClearServices() {
+    ShillServiceClient::Get()->GetTestInterface()->ClearServices();
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Helper function to create a VPN network using NetworkConfigurationHandler.
+  void CreateTestShillConfiguration(const char* vpn_provider_type,
+                                    bool is_managed) {
+    base::Value properties(base::Value::Type::DICTIONARY);
+
+    properties.SetKey(shill::kGuidProperty, base::Value("vpn_guid"));
+    properties.SetKey(shill::kTypeProperty, base::Value(shill::kTypeVPN));
+    properties.SetKey(shill::kStateProperty, base::Value(shill::kStateIdle));
+    properties.SetKey(shill::kProviderHostProperty, base::Value("vpn_host"));
+    properties.SetKey(shill::kProviderTypeProperty,
+                      base::Value(vpn_provider_type));
+    properties.SetKey(
+        shill::kProfileProperty,
+        base::Value(NetworkProfileHandler::GetSharedProfilePath()));
+
+    if (is_managed) {
+      properties.SetKey(shill::kONCSourceProperty,
+                        base::Value(shill::kONCSourceDevicePolicy));
+      std::unique_ptr<NetworkUIData> ui_data = NetworkUIData::CreateFromONC(
+          ::onc::ONCSource::ONC_SOURCE_DEVICE_POLICY);
+      properties.SetKey(shill::kUIDataProperty,
+                        base::Value(ui_data->GetAsJson()));
+    }
+
+    NetworkHandler::Get()
+        ->network_configuration_handler()
+        ->CreateShillConfiguration(properties, base::DoNothing(),
+                                   base::BindOnce(&ErrorCallback));
+    base::RunLoop().RunUntilIdle();
+  }
+
+  void ExpectConfigurationSourceCounts(const char* histogram,
+                                       size_t manual_count,
+                                       size_t policy_count) {
+    histogram_tester_->ExpectBucketCount<
+        VpnNetworkMetricsHelper::VPNConfigurationSource>(
+        histogram,
+        VpnNetworkMetricsHelper::VPNConfigurationSource::kConfiguredManually,
+        manual_count);
+    histogram_tester_->ExpectBucketCount<
+        VpnNetworkMetricsHelper::VPNConfigurationSource>(
+        histogram,
+        VpnNetworkMetricsHelper::VPNConfigurationSource::kConfiguredByPolicy,
+        policy_count);
+    histogram_tester_->ExpectTotalCount(histogram, manual_count + policy_count);
+  }
+
+ private:
+  base::test::TaskEnvironment task_environment_;
+  std::unique_ptr<base::HistogramTester> histogram_tester_;
+  std::unique_ptr<NetworkHandlerTestHelper> network_handler_test_helper_;
+};
+
+TEST_F(VpnNetworkMetricsHelperTest, LogVpnVPNConfigurationSource) {
+  const std::vector<std::pair<const char*, const char*>>
+      kProvidersAndHistograms{{
+          {shill::kProviderL2tpIpsec,
+           kVpnHistogramConfigurationSourceL2tpIpsec},
+          {shill::kProviderArcVpn, kVpnHistogramConfigurationSourceArc},
+          {shill::kProviderOpenVpn, kVpnHistogramConfigurationSourceOpenVpn},
+          {shill::kProviderThirdPartyVpn,
+           kVpnHistogramConfigurationSourceThirdParty},
+          {shill::kProviderWireGuard,
+           kVpnHistogramConfigurationSourceWireGuard},
+      }};
+
+  for (const auto& it : kProvidersAndHistograms) {
+    ExpectConfigurationSourceCounts(it.first, /*manual_count=*/0,
+                                    /*policy_count=*/0);
+
+    CreateTestShillConfiguration(it.first, /*is_managed=*/false);
+    ExpectConfigurationSourceCounts(it.second, /*manual_count=*/1,
+                                    /*policy_count=*/0);
+    ClearServices();
+
+    CreateTestShillConfiguration(it.first, /*is_managed=*/true);
+    ExpectConfigurationSourceCounts(it.second, /*manual_count=*/1,
+                                    /*policy_count=*/1);
+    ClearServices();
+  }
+}
+
+}  // namespace chromeos
diff --git a/chromeos/network/network_configuration_handler.cc b/chromeos/network/network_configuration_handler.cc
index 13cbe1ae..9fe9c29 100644
--- a/chromeos/network/network_configuration_handler.cc
+++ b/chromeos/network/network_configuration_handler.cc
@@ -540,21 +540,31 @@
   // from a previous connection attempt.
   network_state_handler_->ClearLastErrorForNetwork(service_path.value());
 
+  // |configure_callbacks_| will get triggered when NetworkStateHandler notifies
+  // this that a state list update has occurred, which will be triggered by the
+  // following RequestUpdateForNetwork call. |service_path| is unique per
+  // configuration.
+  configure_callbacks_.insert(std::make_pair(
+      service_path.value(),
+      base::BindOnce(&NetworkConfigurationHandler::NotifyConfigurationCompleted,
+                     weak_ptr_factory_.GetWeakPtr(), std::move(callback))));
+
   // Shill should send a network list update, but to ensure that Shill sends
   // the newly configured properties immediately, request an update here.
   network_state_handler_->RequestUpdateForNetwork(service_path.value());
+}
 
+void NetworkConfigurationHandler::NotifyConfigurationCompleted(
+    network_handler::ServiceResultCallback callback,
+    const std::string& service_path,
+    const std::string& guid) {
   for (auto& observer : observers_)
-    observer.OnConfigurationCreated(service_path.value(), guid);
+    observer.OnConfigurationCreated(service_path, guid);
 
   if (callback.is_null())
     return;
 
-  // |configure_callbacks_| will get triggered when NetworkStateHandler
-  // notifies this that a state list update has occurred. |service_path|
-  // is unique per configuration.
-  configure_callbacks_.insert(
-      std::make_pair(service_path.value(), std::move(callback)));
+  std::move(callback).Run(service_path, guid);
 }
 
 void NetworkConfigurationHandler::ProfileEntryDeleterCompleted(
diff --git a/chromeos/network/network_configuration_handler.h b/chromeos/network/network_configuration_handler.h
index 37434f4..0ba2f6d1 100644
--- a/chromeos/network/network_configuration_handler.h
+++ b/chromeos/network/network_configuration_handler.h
@@ -150,14 +150,23 @@
   void Init(NetworkStateHandler* network_state_handler,
             NetworkDeviceHandler* network_device_handler);
 
-  // Called when a configuration completes. This will wait for the cached
-  // state (NetworkStateHandler) to update before triggering the callback.
+  // Called when a configuration completes. This will use
+  // NotifyConfigurationCompleted to defer notifying observers that a
+  // configuration was completed and invoking |callback| until the cached state
+  // (NetworkStateHandler) to update before triggering the callback.
   void ConfigurationCompleted(const std::string& profile_path,
                               const std::string& guid,
                               base::Value configure_properties,
                               network_handler::ServiceResultCallback callback,
                               const dbus::ObjectPath& service_path);
 
+  // Used by ConfigurationCompleted to defer notifying observers and invoking
+  // the provided callback.
+  void NotifyConfigurationCompleted(
+      network_handler::ServiceResultCallback callback,
+      const std::string& service_path,
+      const std::string& guid);
+
   void ConfigurationFailed(network_handler::ErrorCallback error_callback,
                            const std::string& dbus_error_name,
                            const std::string& dbus_error_message);
diff --git a/chromeos/network/network_handler.cc b/chromeos/network/network_handler.cc
index 3eb986b..2a4cc87 100644
--- a/chromeos/network/network_handler.cc
+++ b/chromeos/network/network_handler.cc
@@ -17,7 +17,9 @@
 #include "chromeos/network/client_cert_resolver.h"
 #include "chromeos/network/geolocation_handler.h"
 #include "chromeos/network/managed_network_configuration_handler_impl.h"
+#include "chromeos/network/metrics/connection_info_metrics_logger.h"
 #include "chromeos/network/metrics/esim_policy_login_metrics_logger.h"
+#include "chromeos/network/metrics/vpn_network_metrics_helper.h"
 #include "chromeos/network/network_activation_handler_impl.h"
 #include "chromeos/network/network_cert_loader.h"
 #include "chromeos/network/network_cert_migrator.h"
@@ -59,6 +61,8 @@
     esim_policy_login_metrics_logger_.reset(new ESimPolicyLoginMetricsLogger());
   }
   cellular_metrics_logger_.reset(new CellularMetricsLogger());
+  connection_info_metrics_logger_.reset(new ConnectionInfoMetricsLogger());
+  vpn_network_metrics_helper_.reset(new VpnNetworkMetricsHelper());
   if (NetworkCertLoader::IsInitialized()) {
     network_cert_migrator_.reset(new NetworkCertMigrator());
     client_cert_resolver_.reset(new ClientCertResolver());
@@ -117,6 +121,9 @@
   cellular_metrics_logger_->Init(network_state_handler_.get(),
                                  network_connection_handler_.get(),
                                  cellular_esim_profile_handler_.get());
+  connection_info_metrics_logger_->Init(network_state_handler_.get(),
+                                        network_connection_handler_.get());
+  vpn_network_metrics_helper_->Init(network_configuration_handler_.get());
   if (network_cert_migrator_)
     network_cert_migrator_->Init(network_state_handler_.get());
   if (client_cert_resolver_) {
diff --git a/chromeos/network/network_handler.h b/chromeos/network/network_handler.h
index dfe88316..7682caf5 100644
--- a/chromeos/network/network_handler.h
+++ b/chromeos/network/network_handler.h
@@ -24,6 +24,7 @@
 class CellularMetricsLogger;
 class CellularPolicyHandler;
 class ClientCertResolver;
+class ConnectionInfoMetricsLogger;
 class ESimPolicyLoginMetricsLogger;
 class GeolocationHandler;
 class ManagedNetworkConfigurationHandler;
@@ -42,6 +43,7 @@
 class ProhibitedTechnologiesHandler;
 class StubCellularNetworksProvider;
 class UIProxyConfigService;
+class VpnNetworkMetricsHelper;
 
 // Class for handling initialization and access to chromeos network handlers.
 // This class should NOT be used in unit tests. Instead, construct individual
@@ -110,6 +112,8 @@
   ProhibitedTechnologiesHandler* prohibited_technologies_handler();
 
  private:
+  friend class ConnectionInfoMetricsLoggerTest;
+
   NetworkHandler();
   virtual ~NetworkHandler();
 
@@ -134,8 +138,10 @@
       cellular_esim_uninstall_handler_;
   std::unique_ptr<CellularPolicyHandler> cellular_policy_handler_;
   std::unique_ptr<CellularMetricsLogger> cellular_metrics_logger_;
+  std::unique_ptr<ConnectionInfoMetricsLogger> connection_info_metrics_logger_;
   std::unique_ptr<ESimPolicyLoginMetricsLogger>
       esim_policy_login_metrics_logger_;
+  std::unique_ptr<VpnNetworkMetricsHelper> vpn_network_metrics_helper_;
   std::unique_ptr<NetworkCertMigrator> network_cert_migrator_;
   std::unique_ptr<ClientCertResolver> client_cert_resolver_;
   std::unique_ptr<AutoConnectHandler> auto_connect_handler_;
diff --git a/chromeos/network/policy_applicator.cc b/chromeos/network/policy_applicator.cc
index 75e6b64..7d25520 100644
--- a/chromeos/network/policy_applicator.cc
+++ b/chromeos/network/policy_applicator.cc
@@ -57,6 +57,12 @@
   return guid_value->GetString();
 }
 
+bool IsCellularPolicy(const base::DictionaryValue& onc_config) {
+  const std::string* type =
+      onc_config.FindStringKey(::onc::network_config::kType);
+  return type && *type == ::onc::network_type::kCellular;
+}
+
 const std::string* GetSMDPAddressFromONC(
     const base::DictionaryValue& onc_config) {
   const std::string* type =
@@ -441,9 +447,9 @@
     VLOG(1) << "Creating new configuration managed by policy " << *it
             << " in profile " << profile_.ToDebugString() << ".";
 
-    if (features::IsESimPolicyEnabled()) {
+    if (IsCellularPolicy(*network_policy)) {
       const std::string* smdp_address = GetSMDPAddressFromONC(*network_policy);
-      if (smdp_address) {
+      if (features::IsESimPolicyEnabled() && smdp_address) {
         NET_LOG(EVENT)
             << "Found ONC configuration with SMDP: " << *smdp_address
             << ". Start installing policy eSim profile with ONC config: "
@@ -453,13 +459,17 @@
         else
           NET_LOG(ERROR) << "Unable to install eSIM. CellularPolicyHandler not "
                             "initialized.";
-
-        it = remaining_policy_guids_.erase(it);
-        if (remaining_policy_guids_.empty()) {
-          NotifyConfigurationHandlerAndFinish();
-        }
-        continue;
+      } else {
+        NET_LOG(EVENT) << "Skip installing policy eSIM either because "
+                          "the eSIM policy feature is not enabled or the SMDP "
+                          "address is missing from ONC.";
       }
+
+      it = remaining_policy_guids_.erase(it);
+      if (remaining_policy_guids_.empty()) {
+        NotifyConfigurationHandlerAndFinish();
+      }
+      continue;
     }
 
     base::Value shill_dictionary = policy_util::CreateShillConfiguration(
diff --git a/chromeos/services/bluetooth_config/BUILD.gn b/chromeos/services/bluetooth_config/BUILD.gn
index c8ffa64..fe6d89a 100644
--- a/chromeos/services/bluetooth_config/BUILD.gn
+++ b/chromeos/services/bluetooth_config/BUILD.gn
@@ -37,6 +37,10 @@
     "device_pairing_handler.h",
     "device_pairing_handler_impl.cc",
     "device_pairing_handler_impl.h",
+    "discovered_devices_provider.cc",
+    "discovered_devices_provider.h",
+    "discovered_devices_provider_impl.cc",
+    "discovered_devices_provider_impl.h",
     "discovery_session_manager.cc",
     "discovery_session_manager.h",
     "discovery_session_manager_impl.cc",
@@ -110,6 +114,8 @@
     "fake_device_pairing_delegate.h",
     "fake_device_pairing_handler.cc",
     "fake_device_pairing_handler.h",
+    "fake_discovered_devices_provider.cc",
+    "fake_discovered_devices_provider.h",
     "fake_discovery_session_manager.cc",
     "fake_discovery_session_manager.h",
     "fake_fast_pair_delegate.cc",
@@ -147,6 +153,7 @@
     "device_name_manager_impl_unittest.cc",
     "device_operation_handler_impl_unittest.cc",
     "device_pairing_handler_impl_unittest.cc",
+    "discovered_devices_provider_impl_unittest.cc",
     "discovery_session_manager_impl_unittest.cc",
     "system_properties_provider_impl_unittest.cc",
   ]
diff --git a/chromeos/services/bluetooth_config/cros_bluetooth_config.cc b/chromeos/services/bluetooth_config/cros_bluetooth_config.cc
index 779b7f2f..f8fd335 100644
--- a/chromeos/services/bluetooth_config/cros_bluetooth_config.cc
+++ b/chromeos/services/bluetooth_config/cros_bluetooth_config.cc
@@ -8,6 +8,7 @@
 #include "chromeos/services/bluetooth_config/bluetooth_power_controller.h"
 #include "chromeos/services/bluetooth_config/device_name_manager.h"
 #include "chromeos/services/bluetooth_config/device_operation_handler.h"
+#include "chromeos/services/bluetooth_config/discovered_devices_provider.h"
 #include "chromeos/services/bluetooth_config/discovery_session_manager.h"
 #include "chromeos/services/bluetooth_config/fast_pair_delegate.h"
 #include "chromeos/services/bluetooth_config/initializer.h"
@@ -37,10 +38,12 @@
               device_cache_.get())),
       bluetooth_device_status_notifier_(
           initializer.CreateBluetoothDeviceStatusNotifier(device_cache_.get())),
+      discovered_devices_provider_(
+          initializer.CreateDiscoveredDevicesProvider(device_cache_.get())),
       discovery_session_manager_(initializer.CreateDiscoverySessionManager(
           adapter_state_controller_.get(),
           bluetooth_adapter,
-          device_cache_.get())),
+          discovered_devices_provider_.get())),
       device_operation_handler_(initializer.CreateDeviceOperationHandler(
           adapter_state_controller_.get(),
           bluetooth_adapter)),
diff --git a/chromeos/services/bluetooth_config/cros_bluetooth_config.h b/chromeos/services/bluetooth_config/cros_bluetooth_config.h
index de8d3176..e85b24f6 100644
--- a/chromeos/services/bluetooth_config/cros_bluetooth_config.h
+++ b/chromeos/services/bluetooth_config/cros_bluetooth_config.h
@@ -27,6 +27,7 @@
 class DeviceCache;
 class DeviceNameManager;
 class DeviceOperationHandler;
+class DiscoveredDevicesProvider;
 class DiscoverySessionManager;
 class FastPairDelegate;
 class Initializer;
@@ -76,6 +77,7 @@
   std::unique_ptr<SystemPropertiesProvider> system_properties_provider_;
   std::unique_ptr<BluetoothDeviceStatusNotifier>
       bluetooth_device_status_notifier_;
+  std::unique_ptr<DiscoveredDevicesProvider> discovered_devices_provider_;
   std::unique_ptr<DiscoverySessionManager> discovery_session_manager_;
   std::unique_ptr<DeviceOperationHandler> device_operation_handler_;
   FastPairDelegate* fast_pair_delegate_ = nullptr;
diff --git a/chromeos/services/bluetooth_config/discovered_devices_provider.cc b/chromeos/services/bluetooth_config/discovered_devices_provider.cc
new file mode 100644
index 0000000..a0b345a
--- /dev/null
+++ b/chromeos/services/bluetooth_config/discovered_devices_provider.cc
@@ -0,0 +1,28 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/bluetooth_config/discovered_devices_provider.h"
+
+namespace chromeos {
+namespace bluetooth_config {
+
+DiscoveredDevicesProvider::DiscoveredDevicesProvider() = default;
+
+DiscoveredDevicesProvider::~DiscoveredDevicesProvider() = default;
+
+void DiscoveredDevicesProvider::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void DiscoveredDevicesProvider::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void DiscoveredDevicesProvider::NotifyDiscoveredDevicesListChanged() {
+  for (auto& observer : observers_)
+    observer.OnDiscoveredDevicesListChanged();
+}
+
+}  // namespace bluetooth_config
+}  // namespace chromeos
diff --git a/chromeos/services/bluetooth_config/discovered_devices_provider.h b/chromeos/services/bluetooth_config/discovered_devices_provider.h
new file mode 100644
index 0000000..f63f4b4
--- /dev/null
+++ b/chromeos/services/bluetooth_config/discovered_devices_provider.h
@@ -0,0 +1,49 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_BLUETOOTH_CONFIG_DISCOVERED_DEVICES_PROVIDER_H_
+#define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_DISCOVERED_DEVICES_PROVIDER_H_
+
+#include <vector>
+
+#include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
+
+namespace chromeos {
+namespace bluetooth_config {
+
+// Provides clients with the list of unpaired devices found during a discovery
+// session.
+class DiscoveredDevicesProvider {
+ public:
+  class Observer : public base::CheckedObserver {
+   public:
+    ~Observer() override = default;
+
+    // Invoked when the list of discovered devices has changed. This callback is
+    // used when a device has been added/removed from the list, or when one or
+    // more properties of a device in the list has changed.
+    virtual void OnDiscoveredDevicesListChanged() = 0;
+  };
+
+  virtual ~DiscoveredDevicesProvider();
+
+  // Returns the list of discovered devices.
+  virtual std::vector<mojom::BluetoothDevicePropertiesPtr>
+  GetDiscoveredDevices() const = 0;
+
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+ protected:
+  DiscoveredDevicesProvider();
+
+  void NotifyDiscoveredDevicesListChanged();
+
+  base::ObserverList<Observer> observers_;
+};
+
+}  // namespace bluetooth_config
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_DISCOVERED_DEVICES_PROVIDER_H_
diff --git a/chromeos/services/bluetooth_config/discovered_devices_provider_impl.cc b/chromeos/services/bluetooth_config/discovered_devices_provider_impl.cc
new file mode 100644
index 0000000..4b387e3
--- /dev/null
+++ b/chromeos/services/bluetooth_config/discovered_devices_provider_impl.cc
@@ -0,0 +1,114 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/bluetooth_config/discovered_devices_provider_impl.h"
+
+#include <algorithm>
+
+namespace chromeos {
+namespace bluetooth_config {
+
+// static
+const base::TimeDelta DiscoveredDevicesProviderImpl::kNotificationDelay =
+    base::Seconds(5);
+
+DiscoveredDevicesProviderImpl::DiscoveredDevicesProviderImpl(
+    DeviceCache* device_cache)
+    : device_cache_(device_cache) {
+  device_cache_observation_.Observe(device_cache_);
+}
+
+DiscoveredDevicesProviderImpl::~DiscoveredDevicesProviderImpl() = default;
+
+std::vector<mojom::BluetoothDevicePropertiesPtr>
+DiscoveredDevicesProviderImpl::GetDiscoveredDevices() const {
+  std::vector<mojom::BluetoothDevicePropertiesPtr> discovered_devices;
+  for (const auto& discovered_device : discovered_devices_)
+    discovered_devices.push_back(discovered_device->Clone());
+  return discovered_devices;
+}
+
+void DiscoveredDevicesProviderImpl::OnUnpairedDevicesListChanged() {
+  // When the list of unpaired devices changes, this method implements the
+  // following logic in effort to limit the frequency of device position changes
+  // clients observe:
+  //
+  // If a device has been added, it's appended to the end of
+  // the list and clients are notified. If no timer is currently running, after
+  // |kNotificationDelay|, the list is sorted, and clients are notified again.
+  //
+  // If a device has been updated, it's properties are updated but its position
+  // un-updated, and clients are notified. If no timer is currently running,
+  // after |kNotificationDelay|, the list is sorted, and clients are notified
+  // again.
+  //
+  // If a device has been removed, it's removed from the list and clients are
+  // notified. If no timer is currently running, after
+  // |kNotificationDelay|, the list is sorted, and clients are notified again.
+  // This last sorting and notification are unnecessary but simplify this
+  // method.
+  std::vector<mojom::BluetoothDevicePropertiesPtr> unpaired_devices =
+      device_cache_->GetUnpairedDevices();
+  std::unordered_set<std::string> unpaired_device_ids;
+
+  // Iterate through |unpaired_devices| to find which devices have been added
+  // and which have been updated.
+  for (const auto& unpaired_device : unpaired_devices) {
+    // Save |unpaired_device|'s id for later to check for device removals.
+    unpaired_device_ids.insert(unpaired_device->id);
+
+    // Check if |discovered_devices_| contains |unpaired_device_|.
+    const auto it =
+        std::find_if(discovered_devices_.begin(), discovered_devices_.end(),
+                     [&unpaired_device](const auto& discovered_device) {
+                       return discovered_device->id == unpaired_device->id;
+                     });
+    if (it == discovered_devices_.end()) {
+      // If it doesn't contain the device, the device was added. Append to end
+      // of the list.
+      discovered_devices_.push_back(unpaired_device.Clone());
+    } else {
+      // If it does contain the device, update the device.
+      *it = unpaired_device.Clone();
+    }
+  }
+
+  // Remove any devices in |discovered_devices_| that are not found in
+  // |unpaired_devices|.
+  discovered_devices_.erase(
+      std::remove_if(discovered_devices_.begin(), discovered_devices_.end(),
+                     [&unpaired_device_ids](const auto& discovered_device) {
+                       return unpaired_device_ids.find(discovered_device->id) ==
+                              unpaired_device_ids.end();
+                     }),
+      discovered_devices_.end());
+
+  // Immediately notify clients of changes before sorting.
+  NotifyDiscoveredDevicesListChanged();
+
+  // Don't set the timer if one is already running.
+  if (notification_delay_timer_.IsRunning())
+    return;
+
+  // Wait |kNotificationDelay| to sort the list and inform clients that the list
+  // of discovered devices has changed.
+  notification_delay_timer_.Start(
+      FROM_HERE, kNotificationDelay,
+      base::BindOnce(
+          &DiscoveredDevicesProviderImpl::SortDiscoveredDevicesAndNotify,
+          weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DiscoveredDevicesProviderImpl::SortDiscoveredDevicesAndNotify() {
+  // |device_cache_| will have the most recent list of sorted discovered
+  // devices. Copy this over to |discovered_devices_| instead of needing to sort
+  // it ourselves.
+  discovered_devices_.clear();
+  for (const auto& unpaired_device : device_cache_->GetUnpairedDevices())
+    discovered_devices_.push_back(unpaired_device.Clone());
+  NotifyDiscoveredDevicesListChanged();
+}
+
+}  // namespace bluetooth_config
+}  // namespace chromeos
diff --git a/chromeos/services/bluetooth_config/discovered_devices_provider_impl.h b/chromeos/services/bluetooth_config/discovered_devices_provider_impl.h
new file mode 100644
index 0000000..5fcd938
--- /dev/null
+++ b/chromeos/services/bluetooth_config/discovered_devices_provider_impl.h
@@ -0,0 +1,61 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_BLUETOOTH_CONFIG_DISCOVERED_DEVICES_PROVIDER_IMPL_H_
+#define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_DISCOVERED_DEVICES_PROVIDER_IMPL_H_
+
+#include "chromeos/services/bluetooth_config/discovered_devices_provider.h"
+
+#include "base/memory/weak_ptr.h"
+#include "base/scoped_observation.h"
+#include "chromeos/services/bluetooth_config/device_cache.h"
+
+namespace chromeos {
+namespace bluetooth_config {
+
+// Concrete DiscoveredDevicesProvider implementation that batches discovered
+// devices list updates. If the device list has changed, this implementation
+// waits |kNotificationDelay| before sorting and notifying clients that the list
+// has changed. This is to reduce the frequency of changes to the device list in
+// UI surfaces, giving users more time to view the list between updates.
+class DiscoveredDevicesProviderImpl : public DiscoveredDevicesProvider,
+                                      public DeviceCache::Observer {
+ public:
+  explicit DiscoveredDevicesProviderImpl(DeviceCache* device_cache);
+  ~DiscoveredDevicesProviderImpl() override;
+
+ private:
+  friend class DiscoveredDevicesProviderImplTest;
+
+  // Delay from when the unpaired devices list has changed and when clients are
+  // notified.
+  static const base::TimeDelta kNotificationDelay;
+
+  // DiscoveredDevicesProvider:
+  std::vector<mojom::BluetoothDevicePropertiesPtr> GetDiscoveredDevices()
+      const override;
+
+  // DeviceCache::Observer:
+  void OnUnpairedDevicesListChanged() override;
+
+  // Method invoked once |notification_delay_timer_| expires that sorts
+  // |discovered_devices_|, then notifies clients of the change.
+  void SortDiscoveredDevicesAndNotify();
+
+  base::OneShotTimer notification_delay_timer_;
+
+  std::vector<mojom::BluetoothDevicePropertiesPtr> discovered_devices_;
+
+  DeviceCache* device_cache_;
+
+  base::ScopedObservation<DeviceCache, DeviceCache::Observer>
+      device_cache_observation_{this};
+
+  base::WeakPtrFactory<DiscoveredDevicesProviderImpl> weak_ptr_factory_{this};
+};
+
+}  // namespace bluetooth_config
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_DISCOVERED_DEVICES_PROVIDER_IMPL_H_
diff --git a/chromeos/services/bluetooth_config/discovered_devices_provider_impl_unittest.cc b/chromeos/services/bluetooth_config/discovered_devices_provider_impl_unittest.cc
new file mode 100644
index 0000000..e6f13b2
--- /dev/null
+++ b/chromeos/services/bluetooth_config/discovered_devices_provider_impl_unittest.cc
@@ -0,0 +1,367 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/bluetooth_config/discovered_devices_provider_impl.h"
+
+#include "base/strings/strcat.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/test/task_environment.h"
+#include "chromeos/services/bluetooth_config/device_conversion_util.h"
+#include "chromeos/services/bluetooth_config/fake_adapter_state_controller.h"
+#include "chromeos/services/bluetooth_config/fake_device_cache.h"
+#include "device/bluetooth/test/mock_bluetooth_device.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace bluetooth_config {
+namespace {
+
+using DiscoveredDevicesList = std::vector<mojom::BluetoothDevicePropertiesPtr>;
+using NiceMockDevice =
+    std::unique_ptr<testing::NiceMock<device::MockBluetoothDevice>>;
+
+const uint32_t kTestBluetoothClass = 1337u;
+const char kTestBluetoothName[] = "testName";
+
+class FakeObserver : public DiscoveredDevicesProvider::Observer {
+ public:
+  FakeObserver() = default;
+  ~FakeObserver() override = default;
+
+  size_t num_discovered_devices_list_changed_calls() const {
+    return num_discovered_devices_list_changed_calls_;
+  }
+
+ private:
+  // DiscoveredDevicesProvider::Observer:
+  void OnDiscoveredDevicesListChanged() override {
+    ++num_discovered_devices_list_changed_calls_;
+  }
+
+  size_t num_discovered_devices_list_changed_calls_ = 0u;
+};
+
+}  // namespace
+
+class DiscoveredDevicesProviderImplTest : public testing::Test {
+ protected:
+  DiscoveredDevicesProviderImplTest()
+      : task_environment_(
+            base::test::SingleThreadTaskEnvironment::MainThreadType::UI,
+            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  DiscoveredDevicesProviderImplTest(const DiscoveredDevicesProviderImplTest&) =
+      delete;
+  DiscoveredDevicesProviderImplTest& operator=(
+      const DiscoveredDevicesProviderImplTest&) = delete;
+  ~DiscoveredDevicesProviderImplTest() override = default;
+
+  // testing::Test:
+  void SetUp() override {
+    discovered_devices_provider_ =
+        std::make_unique<DiscoveredDevicesProviderImpl>(&fake_device_cache_);
+    discovered_devices_provider_->AddObserver(&fake_observer_);
+  }
+
+  void InsertDevice(int index, std::string* id_out) {
+    // We use the number of devices created in this test as the address.
+    std::string address = base::NumberToString(num_devices_created_);
+    ++num_devices_created_;
+
+    // Mock devices have their ID set to "${address}-Identifier".
+    *id_out = base::StrCat({address, "-Identifier"});
+
+    auto mock_device =
+        std::make_unique<testing::NiceMock<device::MockBluetoothDevice>>(
+            /*adapter=*/nullptr, kTestBluetoothClass, kTestBluetoothName,
+            address, /*paired=*/false, /*connected=*/false);
+    ON_CALL(*mock_device, GetDeviceType())
+        .WillByDefault(testing::Return(device::BluetoothDeviceType::AUDIO));
+    mock_devices_.insert(mock_devices_.begin() + index, std::move(mock_device));
+
+    UpdateDeviceCache();
+  }
+
+  void UpdateDevicePosition(const std::string& device_id, int new_index) {
+    auto it = FindDevice(device_id);
+    EXPECT_TRUE(it != mock_devices_.end());
+    NiceMockDevice device = std::move(*it);
+
+    mock_devices_.erase(it);
+    mock_devices_.insert(mock_devices_.begin() + new_index, std::move(device));
+
+    UpdateDeviceCache();
+  }
+
+  void UpdateDeviceType(const std::string& device_id,
+                        device::BluetoothDeviceType new_type) {
+    auto it = FindDevice(device_id);
+    EXPECT_TRUE(it != mock_devices_.end());
+    ON_CALL(**it, GetDeviceType()).WillByDefault(testing::Return(new_type));
+
+    UpdateDeviceCache();
+  }
+
+  void RemoveDevice(const std::string& device_id) {
+    auto it = FindDevice(device_id);
+    EXPECT_TRUE(it != mock_devices_.end());
+    mock_devices_.erase(it);
+
+    UpdateDeviceCache();
+  }
+
+  int64_t GetNotificationDelaySeconds() {
+    return DiscoveredDevicesProviderImpl::kNotificationDelay.InSeconds();
+  }
+
+  void FastForward(int64_t seconds) {
+    task_environment_.FastForwardBy(base::Seconds(seconds));
+  }
+
+  DiscoveredDevicesList GetDiscoveredDevices() {
+    return discovered_devices_provider_->GetDiscoveredDevices();
+  }
+
+  size_t GetNumDiscoveredDevicesListObserverEvents() const {
+    return fake_observer_.num_discovered_devices_list_changed_calls();
+  }
+
+  void AssertNumEventsAndDeviceList(size_t num_observer_events,
+                                    std::vector<std::string> device_list) {
+    EXPECT_EQ(num_observer_events, GetNumDiscoveredDevicesListObserverEvents());
+    DiscoveredDevicesList discovered_devices = GetDiscoveredDevices();
+    EXPECT_EQ(device_list.size(), discovered_devices.size());
+    for (size_t i = 0; i < device_list.size(); i++) {
+      EXPECT_EQ(device_list[i], discovered_devices[i]->id);
+    }
+  }
+
+ private:
+  std::vector<NiceMockDevice>::iterator FindDevice(
+      const std::string& device_id) {
+    return std::find_if(mock_devices_.begin(), mock_devices_.end(),
+                        [&device_id](const NiceMockDevice& device) {
+                          return device_id == device->GetIdentifier();
+                        });
+  }
+
+  // Copies the list of devices in |mock_devices_| to device's caches unpaired
+  // devices.
+  void UpdateDeviceCache() {
+    std::vector<mojom::BluetoothDevicePropertiesPtr> unpaired_devices;
+    for (auto& device : mock_devices_) {
+      unpaired_devices.push_back(
+          GenerateBluetoothDeviceMojoProperties(device.get()));
+    }
+    fake_device_cache_.SetUnpairedDevices(std::move(unpaired_devices));
+  }
+
+  base::test::TaskEnvironment task_environment_;
+
+  std::vector<NiceMockDevice> mock_devices_;
+  size_t num_devices_created_ = 0u;
+
+  FakeAdapterStateController fake_adapter_state_controller_;
+  FakeDeviceCache fake_device_cache_{&fake_adapter_state_controller_};
+  FakeObserver fake_observer_;
+
+  std::unique_ptr<DiscoveredDevicesProvider> discovered_devices_provider_;
+};
+
+TEST_F(DiscoveredDevicesProviderImplTest, AddUnpairedDevices) {
+  EXPECT_EQ(0u, GetNumDiscoveredDevicesListObserverEvents());
+  EXPECT_TRUE(GetDiscoveredDevices().empty());
+
+  // Add a device. This should notify observers and start the timer.
+  std::string device_id1;
+  InsertDevice(/*index=*/0, &device_id1);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/1u, {device_id1});
+
+  // Simulate |seconds_forward| seconds passing.
+  int seconds_forward = 1;
+  FastForward(seconds_forward);
+
+  // Insert a second device at the front of the list. Observers should be
+  // notified, but the second device placed at the end of the list.
+  std::string device_id2;
+  InsertDevice(/*index=*/0, &device_id2);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/2u,
+                               {device_id1, device_id2});
+
+  // Simulate the notification delay passing since the first device was added.
+  // Observers be notified again with the correctly sorted list.
+  FastForward(GetNotificationDelaySeconds() - seconds_forward);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/3u,
+                               {device_id2, device_id1});
+
+  // Simulate moving to the time to where the second device's timer would
+  // expire, if it was set. Nothing new should have occurred since the timer
+  // shouldn't have been set.
+  FastForward(/*seconds=*/seconds_forward);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/3u,
+                               {device_id2, device_id1});
+
+  // Insert a third device at the front of the list. Observers should be
+  // notified, but the third device placed at the end of the list.
+  std::string device_id3;
+  InsertDevice(/*index=*/0, &device_id3);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/4u,
+                               {device_id2, device_id1, device_id3});
+
+  // Simulate the notification delay passing. Observers should be notified again
+  // with the correctly sorted list.
+  FastForward(GetNotificationDelaySeconds());
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/5u,
+                               {device_id3, device_id2, device_id1});
+}
+
+TEST_F(DiscoveredDevicesProviderImplTest, UpdateUnpairedDevices) {
+  EXPECT_EQ(0u, GetNumDiscoveredDevicesListObserverEvents());
+  EXPECT_TRUE(GetDiscoveredDevices().empty());
+
+  // Add three devices. This should notify observers and start the timer.
+  std::string device_id1;
+  InsertDevice(/*index=*/0, &device_id1);
+  std::string device_id2;
+  InsertDevice(/*index=*/1, &device_id2);
+  std::string device_id3;
+  InsertDevice(/*index=*/2, &device_id3);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/3u,
+                               {device_id1, device_id2, device_id3});
+
+  // Simulate the notification delay passing. Observers should be notified
+  // again.
+  FastForward(GetNotificationDelaySeconds());
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/4u,
+                               {device_id1, device_id2, device_id3});
+
+  // Update device 2. This should notify observers and start the timer, with the
+  // ordering kept same.
+  EXPECT_EQ(mojom::DeviceType::kHeadset,
+            GetDiscoveredDevices()[1]->device_type);
+  UpdateDeviceType(device_id2, device::BluetoothDeviceType::VIDEO);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/5u,
+                               {device_id1, device_id2, device_id3});
+  EXPECT_EQ(mojom::DeviceType::kVideoCamera,
+            GetDiscoveredDevices()[1]->device_type);
+
+  // Simulate the notification delay passing. Observers should be notified again
+  // but nothing changed.
+  FastForward(GetNotificationDelaySeconds());
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/6u,
+                               {device_id1, device_id2, device_id3});
+  EXPECT_EQ(mojom::DeviceType::kVideoCamera,
+            GetDiscoveredDevices()[1]->device_type);
+
+  // Move device 3 to the front of the list. Observers should be notified but
+  // the order should remain the same.
+  UpdateDevicePosition(device_id3, /*new_index=*/0);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/7u,
+                               {device_id1, device_id2, device_id3});
+
+  // Simulate the notification delay passing. Observers should be notified with
+  // the correct ordering.
+  FastForward(GetNotificationDelaySeconds());
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/8u,
+                               {device_id3, device_id1, device_id2});
+
+  // Update device 3. This should notify observers and start the timer, with the
+  // ordering kept same.
+  EXPECT_EQ(mojom::DeviceType::kHeadset,
+            GetDiscoveredDevices()[0]->device_type);
+  UpdateDeviceType(device_id3, device::BluetoothDeviceType::VIDEO);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/9u,
+                               {device_id3, device_id1, device_id2});
+  EXPECT_EQ(mojom::DeviceType::kVideoCamera,
+            GetDiscoveredDevices()[0]->device_type);
+
+  // Simulate |seconds_forward| seconds passing.
+  int seconds_forward = 1;
+  FastForward(seconds_forward);
+
+  // Move device 1 to the front of the list. Observers should be notified but
+  // the order should remain the same.
+  UpdateDevicePosition(device_id1, /*new_index=*/0);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/10u,
+                               {device_id3, device_id1, device_id2});
+  EXPECT_EQ(mojom::DeviceType::kVideoCamera,
+            GetDiscoveredDevices()[0]->device_type);
+
+  // Simulate the notification delay passing since device 3 was updated.
+  // Observers be notified again with the correctly sorted list.
+  FastForward(GetNotificationDelaySeconds() - seconds_forward);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/11u,
+                               {device_id1, device_id3, device_id2});
+  EXPECT_EQ(mojom::DeviceType::kVideoCamera,
+            GetDiscoveredDevices()[1]->device_type);
+
+  // Simulate moving to the time to where the device 1 update's timer would
+  // expire, if it was set. Nothing new should have occurred since the timer
+  // shouldn't have been set.
+  FastForward(/*seconds=*/seconds_forward);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/11u,
+                               {device_id1, device_id3, device_id2});
+  EXPECT_EQ(mojom::DeviceType::kVideoCamera,
+            GetDiscoveredDevices()[1]->device_type);
+}
+
+TEST_F(DiscoveredDevicesProviderImplTest, RemoveUnpairedDevices) {
+  EXPECT_EQ(0u, GetNumDiscoveredDevicesListObserverEvents());
+  EXPECT_TRUE(GetDiscoveredDevices().empty());
+
+  // Add three devices. This should notify observers and start the timer.
+  std::string device_id1;
+  InsertDevice(/*index=*/0, &device_id1);
+  std::string device_id2;
+  InsertDevice(/*index=*/1, &device_id2);
+  std::string device_id3;
+  InsertDevice(/*index=*/2, &device_id3);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/3u,
+                               {device_id1, device_id2, device_id3});
+
+  // Simulate the notification delay passing. Observers should be notified
+  // again.
+  FastForward(GetNotificationDelaySeconds());
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/4u,
+                               {device_id1, device_id2, device_id3});
+
+  // Remove device 2. This should notify observers with the updated list and
+  // start the timer.
+  RemoveDevice(device_id2);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/5u,
+                               {device_id1, device_id3});
+
+  // Simulate the notification delay passing. Observers should be notified
+  // again.
+  FastForward(GetNotificationDelaySeconds());
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/6u,
+                               {device_id1, device_id3});
+
+  // Remove device 1. This should notify observers with the updated list and
+  // start the timer.
+  RemoveDevice(device_id1);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/7u, {device_id3});
+
+  // Simulate |seconds_forward| seconds passing.
+  int seconds_forward = 1;
+  FastForward(seconds_forward);
+
+  // Remove device 3. This should notify observers with the updated list.
+  RemoveDevice(device_id3);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/8u, {});
+
+  // Simulate the notification delay passing since device 1 was removed.
+  // Observers be notified again.
+  FastForward(GetNotificationDelaySeconds() - seconds_forward);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/9u, {});
+
+  // Simulate moving to the time to where the device 3 removal's timer would
+  // expire, if it was set. Nothing new should have occurred since the timer
+  // shouldn't have been set.
+  FastForward(/*seconds=*/seconds_forward);
+  AssertNumEventsAndDeviceList(/*num_observer_events=*/9u, {});
+}
+
+}  // namespace bluetooth_config
+}  // namespace chromeos
diff --git a/chromeos/services/bluetooth_config/discovery_session_manager.cc b/chromeos/services/bluetooth_config/discovery_session_manager.cc
index 9d4c87551..f630da8f 100644
--- a/chromeos/services/bluetooth_config/discovery_session_manager.cc
+++ b/chromeos/services/bluetooth_config/discovery_session_manager.cc
@@ -11,11 +11,12 @@
 
 DiscoverySessionManager::DiscoverySessionManager(
     AdapterStateController* adapter_state_controller,
-    DeviceCache* device_cache)
+    DiscoveredDevicesProvider* discovered_devices_provider)
     : adapter_state_controller_(adapter_state_controller),
-      device_cache_(device_cache) {
+      discovered_devices_provider_(discovered_devices_provider) {
   adapter_state_controller_observation_.Observe(adapter_state_controller_);
-  device_cache_observation_.Observe(device_cache_);
+  discovered_devices_provider_observation_.Observe(
+      discovered_devices_provider_);
   delegates_.set_disconnect_handler(
       base::BindRepeating(&DiscoverySessionManager::OnDelegateDisconnected,
                           base::Unretained(this)));
@@ -47,7 +48,7 @@
     delegates_.Get(id)->OnBluetoothDiscoveryStarted(
         RegisterNewDevicePairingHandler(id));
     delegates_.Get(id)->OnDiscoveredDevicesListChanged(
-        device_cache_->GetUnpairedDevices());
+        discovered_devices_provider_->GetDiscoveredDevices());
   }
 }
 
@@ -81,7 +82,7 @@
 void DiscoverySessionManager::NotifyDiscoveredDevicesListChanged() {
   for (auto& delegate : delegates_) {
     delegate->OnDiscoveredDevicesListChanged(
-        device_cache_->GetUnpairedDevices());
+        discovered_devices_provider_->GetDiscoveredDevices());
   }
 }
 
@@ -94,7 +95,7 @@
   NotifyDiscoveryStoppedAndClearActiveClients();
 }
 
-void DiscoverySessionManager::OnUnpairedDevicesListChanged() {
+void DiscoverySessionManager::OnDiscoveredDevicesListChanged() {
   NotifyDiscoveredDevicesListChanged();
 }
 
diff --git a/chromeos/services/bluetooth_config/discovery_session_manager.h b/chromeos/services/bluetooth_config/discovery_session_manager.h
index 719f5b6f..a9193eb 100644
--- a/chromeos/services/bluetooth_config/discovery_session_manager.h
+++ b/chromeos/services/bluetooth_config/discovery_session_manager.h
@@ -8,8 +8,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "chromeos/services/bluetooth_config/adapter_state_controller.h"
-#include "chromeos/services/bluetooth_config/device_cache.h"
 #include "chromeos/services/bluetooth_config/device_pairing_handler.h"
+#include "chromeos/services/bluetooth_config/discovered_devices_provider.h"
 #include "chromeos/services/bluetooth_config/public/mojom/cros_bluetooth_config.mojom.h"
 #include "mojo/public/cpp/bindings/pending_remote.h"
 #include "mojo/public/cpp/bindings/remote_set.h"
@@ -31,7 +31,7 @@
 // responsible for starting and stopping discovery and does not handle pairing
 // attempts.
 class DiscoverySessionManager : public AdapterStateController::Observer,
-                                public DeviceCache::Observer {
+                                public DiscoveredDevicesProvider::Observer {
  public:
   ~DiscoverySessionManager() override;
 
@@ -42,8 +42,9 @@
       mojo::PendingRemote<mojom::BluetoothDiscoveryDelegate> delegate);
 
  protected:
-  DiscoverySessionManager(AdapterStateController* adapter_state_controller,
-                          DeviceCache* device_cache);
+  DiscoverySessionManager(
+      AdapterStateController* adapter_state_controller,
+      DiscoveredDevicesProvider* discovered_devices_provider);
 
   void NotifyDiscoveryStarted();
   void NotifyDiscoveryStoppedAndClearActiveClients();
@@ -70,8 +71,8 @@
   // AdapterStateController::Observer:
   void OnAdapterStateChanged() override;
 
-  // DeviceCache::Observer:
-  void OnUnpairedDevicesListChanged() override;
+  // DiscoveredDevicesProvider::Observer:
+  void OnDiscoveredDevicesListChanged() override;
 
   // Creates a new DevicePairingHandler for |id| and inserts it into
   // |id_to_pairing_handler_map_|. Returns the remote connected to the handler.
@@ -90,13 +91,14 @@
       id_to_pairing_handler_map_;
 
   AdapterStateController* adapter_state_controller_;
-  DeviceCache* device_cache_;
+  DiscoveredDevicesProvider* discovered_devices_provider_;
 
   base::ScopedObservation<AdapterStateController,
                           AdapterStateController::Observer>
       adapter_state_controller_observation_{this};
-  base::ScopedObservation<DeviceCache, DeviceCache::Observer>
-      device_cache_observation_{this};
+  base::ScopedObservation<DiscoveredDevicesProvider,
+                          DiscoveredDevicesProvider::Observer>
+      discovered_devices_provider_observation_{this};
 
   mojo::RemoteSet<mojom::BluetoothDiscoveryDelegate> delegates_;
 
diff --git a/chromeos/services/bluetooth_config/discovery_session_manager_impl.cc b/chromeos/services/bluetooth_config/discovery_session_manager_impl.cc
index 7eaf2a85d..34233e3 100644
--- a/chromeos/services/bluetooth_config/discovery_session_manager_impl.cc
+++ b/chromeos/services/bluetooth_config/discovery_session_manager_impl.cc
@@ -18,8 +18,9 @@
 DiscoverySessionManagerImpl::DiscoverySessionManagerImpl(
     AdapterStateController* adapter_state_controller,
     scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-    DeviceCache* device_cache)
-    : DiscoverySessionManager(adapter_state_controller, device_cache),
+    DiscoveredDevicesProvider* discovered_devices_provider)
+    : DiscoverySessionManager(adapter_state_controller,
+                              discovered_devices_provider),
       bluetooth_adapter_(std::move(bluetooth_adapter)) {
   adapter_observation_.Observe(bluetooth_adapter_.get());
 }
diff --git a/chromeos/services/bluetooth_config/discovery_session_manager_impl.h b/chromeos/services/bluetooth_config/discovery_session_manager_impl.h
index 83f15f5..5f4f624 100644
--- a/chromeos/services/bluetooth_config/discovery_session_manager_impl.h
+++ b/chromeos/services/bluetooth_config/discovery_session_manager_impl.h
@@ -11,8 +11,8 @@
 #include "base/memory/weak_ptr.h"
 #include "base/scoped_observation.h"
 #include "chromeos/services/bluetooth_config/adapter_state_controller.h"
-#include "chromeos/services/bluetooth_config/device_cache.h"
 #include "chromeos/services/bluetooth_config/device_pairing_handler.h"
+#include "chromeos/services/bluetooth_config/discovered_devices_provider.h"
 #include "chromeos/services/bluetooth_config/discovery_session_manager.h"
 #include "device/bluetooth/bluetooth_adapter.h"
 
@@ -31,7 +31,7 @@
   DiscoverySessionManagerImpl(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-      DeviceCache* device_cache);
+      DiscoveredDevicesProvider* discovered_devices_provider);
   ~DiscoverySessionManagerImpl() override;
 
  private:
diff --git a/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc b/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc
index ec9948a..0be7a21 100644
--- a/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc
+++ b/chromeos/services/bluetooth_config/discovery_session_manager_impl_unittest.cc
@@ -16,9 +16,9 @@
 #include "chromeos/services/bluetooth_config/device_pairing_handler_impl.h"
 #include "chromeos/services/bluetooth_config/fake_adapter_state_controller.h"
 #include "chromeos/services/bluetooth_config/fake_bluetooth_discovery_delegate.h"
-#include "chromeos/services/bluetooth_config/fake_device_cache.h"
 #include "chromeos/services/bluetooth_config/fake_device_pairing_delegate.h"
 #include "chromeos/services/bluetooth_config/fake_device_pairing_handler.h"
+#include "chromeos/services/bluetooth_config/fake_discovered_devices_provider.h"
 #include "device/bluetooth/test/mock_bluetooth_adapter.h"
 #include "device/bluetooth/test/mock_bluetooth_device.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -75,7 +75,8 @@
         }));
 
     discovery_session_manager_ = std::make_unique<DiscoverySessionManagerImpl>(
-        &fake_adapter_state_controller_, mock_adapter_, &fake_device_cache_);
+        &fake_adapter_state_controller_, mock_adapter_,
+        &fake_discovered_devices_provider_);
   }
 
   void TearDown() override {
@@ -150,13 +151,14 @@
             }));
     mock_devices_.push_back(std::move(mock_device));
 
-    // Add the device to the device cache's unpaired devices.
-    std::vector<mojom::BluetoothDevicePropertiesPtr> unpaired_devices;
+    // Add the device to the discovered devices provider's discovered devices.
+    std::vector<mojom::BluetoothDevicePropertiesPtr> discovered_devices;
     for (auto& device : mock_devices_) {
-      unpaired_devices.push_back(
+      discovered_devices.push_back(
           GenerateBluetoothDeviceMojoProperties(device.get()));
     }
-    fake_device_cache_.SetUnpairedDevices(std::move(unpaired_devices));
+    fake_discovered_devices_provider_.SetDiscoveredDevices(
+        std::move(discovered_devices));
     fake_device_pairing_handler_factory_.UpdateActiveHandlerDeviceLists();
     discovery_session_manager_->FlushForTesting();
   }
@@ -228,7 +230,7 @@
   StopScanCallback stop_scan_callback_;
 
   FakeAdapterStateController fake_adapter_state_controller_;
-  FakeDeviceCache fake_device_cache_{&fake_adapter_state_controller_};
+  FakeDiscoveredDevicesProvider fake_discovered_devices_provider_;
   scoped_refptr<testing::NiceMock<device::MockBluetoothAdapter>> mock_adapter_;
   FakeDevicePairingHandlerFactory fake_device_pairing_handler_factory_{*this};
 
diff --git a/chromeos/services/bluetooth_config/fake_discovered_devices_provider.cc b/chromeos/services/bluetooth_config/fake_discovered_devices_provider.cc
new file mode 100644
index 0000000..ac2ae3c2
--- /dev/null
+++ b/chromeos/services/bluetooth_config/fake_discovered_devices_provider.cc
@@ -0,0 +1,29 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/services/bluetooth_config/fake_discovered_devices_provider.h"
+
+namespace chromeos {
+namespace bluetooth_config {
+
+FakeDiscoveredDevicesProvider::FakeDiscoveredDevicesProvider() = default;
+
+FakeDiscoveredDevicesProvider::~FakeDiscoveredDevicesProvider() = default;
+
+void FakeDiscoveredDevicesProvider::SetDiscoveredDevices(
+    std::vector<mojom::BluetoothDevicePropertiesPtr> discovered_devices) {
+  discovered_devices_ = std::move(discovered_devices);
+  NotifyDiscoveredDevicesListChanged();
+}
+
+std::vector<mojom::BluetoothDevicePropertiesPtr>
+FakeDiscoveredDevicesProvider::GetDiscoveredDevices() const {
+  std::vector<mojom::BluetoothDevicePropertiesPtr> discovered_devices;
+  for (const auto& discovered_device : discovered_devices_)
+    discovered_devices.push_back(discovered_device.Clone());
+  return discovered_devices;
+}
+
+}  // namespace bluetooth_config
+}  // namespace chromeos
diff --git a/chromeos/services/bluetooth_config/fake_discovered_devices_provider.h b/chromeos/services/bluetooth_config/fake_discovered_devices_provider.h
new file mode 100644
index 0000000..67b7d67
--- /dev/null
+++ b/chromeos/services/bluetooth_config/fake_discovered_devices_provider.h
@@ -0,0 +1,32 @@
+// Copyright 2021 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_DISCOVERED_DEVICES_PROVIDER_H_
+#define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_DISCOVERED_DEVICES_PROVIDER_H_
+
+#include "chromeos/services/bluetooth_config/discovered_devices_provider.h"
+
+namespace chromeos {
+namespace bluetooth_config {
+
+class FakeDiscoveredDevicesProvider : public DiscoveredDevicesProvider {
+ public:
+  FakeDiscoveredDevicesProvider();
+  ~FakeDiscoveredDevicesProvider() override;
+
+  void SetDiscoveredDevices(
+      std::vector<mojom::BluetoothDevicePropertiesPtr> unpaired_devices);
+
+ private:
+  // DiscoveredDevicesProvider:
+  std::vector<mojom::BluetoothDevicePropertiesPtr> GetDiscoveredDevices()
+      const override;
+
+  std::vector<mojom::BluetoothDevicePropertiesPtr> discovered_devices_;
+};
+
+}  // namespace bluetooth_config
+}  // namespace chromeos
+
+#endif  // CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_DISCOVERED_DEVICES_PROVIDER_H_
diff --git a/chromeos/services/bluetooth_config/fake_discovery_session_manager.cc b/chromeos/services/bluetooth_config/fake_discovery_session_manager.cc
index a5676373..8168eb6 100644
--- a/chromeos/services/bluetooth_config/fake_discovery_session_manager.cc
+++ b/chromeos/services/bluetooth_config/fake_discovery_session_manager.cc
@@ -11,8 +11,9 @@
 
 FakeDiscoverySessionManager::FakeDiscoverySessionManager(
     AdapterStateController* adapter_state_controller,
-    DeviceCache* device_cache)
-    : DiscoverySessionManager(adapter_state_controller, device_cache) {}
+    DiscoveredDevicesProvider* discovered_devices_provider)
+    : DiscoverySessionManager(adapter_state_controller,
+                              discovered_devices_provider) {}
 
 FakeDiscoverySessionManager::~FakeDiscoverySessionManager() = default;
 
diff --git a/chromeos/services/bluetooth_config/fake_discovery_session_manager.h b/chromeos/services/bluetooth_config/fake_discovery_session_manager.h
index eb6b8fa..a7cc704 100644
--- a/chromeos/services/bluetooth_config/fake_discovery_session_manager.h
+++ b/chromeos/services/bluetooth_config/fake_discovery_session_manager.h
@@ -5,7 +5,7 @@
 #ifndef CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_DISCOVERY_SESSION_MANAGER_H_
 #define CHROMEOS_SERVICES_BLUETOOTH_CONFIG_FAKE_DISCOVERY_SESSION_MANAGER_H_
 
-#include "chromeos/services/bluetooth_config/device_cache.h"
+#include "chromeos/services/bluetooth_config/discovered_devices_provider.h"
 #include "chromeos/services/bluetooth_config/discovery_session_manager.h"
 
 namespace chromeos {
@@ -13,8 +13,9 @@
 
 class FakeDiscoverySessionManager : public DiscoverySessionManager {
  public:
-  FakeDiscoverySessionManager(AdapterStateController* adapter_state_controller,
-                              DeviceCache* device_cache);
+  FakeDiscoverySessionManager(
+      AdapterStateController* adapter_state_controller,
+      DiscoveredDevicesProvider* discovered_devices_provider);
   ~FakeDiscoverySessionManager() override;
 
   // Sets whether a discovery session is active and notifies delegates of the
diff --git a/chromeos/services/bluetooth_config/initializer.h b/chromeos/services/bluetooth_config/initializer.h
index b5567ef..f546cc5f 100644
--- a/chromeos/services/bluetooth_config/initializer.h
+++ b/chromeos/services/bluetooth_config/initializer.h
@@ -22,6 +22,7 @@
 class DeviceCache;
 class DeviceNameManager;
 class DeviceOperationHandler;
+class DiscoveredDevicesProvider;
 class DiscoverySessionManager;
 
 // Responsible for initializing the classes needed by the CrosBluetoothConfig
@@ -45,11 +46,13 @@
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
       DeviceNameManager* device_name_manager) = 0;
+  virtual std::unique_ptr<DiscoveredDevicesProvider>
+  CreateDiscoveredDevicesProvider(DeviceCache* device_cache) = 0;
   virtual std::unique_ptr<DiscoverySessionManager>
   CreateDiscoverySessionManager(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-      DeviceCache* device_cache) = 0;
+      DiscoveredDevicesProvider* discovered_devices_provider) = 0;
   virtual std::unique_ptr<DeviceOperationHandler> CreateDeviceOperationHandler(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) = 0;
diff --git a/chromeos/services/bluetooth_config/initializer_impl.cc b/chromeos/services/bluetooth_config/initializer_impl.cc
index 82b3b0b..cfef9d8 100644
--- a/chromeos/services/bluetooth_config/initializer_impl.cc
+++ b/chromeos/services/bluetooth_config/initializer_impl.cc
@@ -10,6 +10,7 @@
 #include "chromeos/services/bluetooth_config/device_cache_impl.h"
 #include "chromeos/services/bluetooth_config/device_name_manager_impl.h"
 #include "chromeos/services/bluetooth_config/device_operation_handler_impl.h"
+#include "chromeos/services/bluetooth_config/discovered_devices_provider_impl.h"
 #include "chromeos/services/bluetooth_config/discovery_session_manager_impl.h"
 
 namespace chromeos {
@@ -53,13 +54,19 @@
                                            device_name_manager);
 }
 
+std::unique_ptr<DiscoveredDevicesProvider>
+InitializerImpl::CreateDiscoveredDevicesProvider(DeviceCache* device_cache) {
+  return std::make_unique<DiscoveredDevicesProviderImpl>(device_cache);
+}
+
 std::unique_ptr<DiscoverySessionManager>
 InitializerImpl::CreateDiscoverySessionManager(
     AdapterStateController* adapter_state_controller,
     scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-    DeviceCache* device_cache) {
+    DiscoveredDevicesProvider* discovered_devices_provider) {
   return std::make_unique<DiscoverySessionManagerImpl>(
-      adapter_state_controller, std::move(bluetooth_adapter), device_cache);
+      adapter_state_controller, std::move(bluetooth_adapter),
+      discovered_devices_provider);
 }
 
 std::unique_ptr<DeviceOperationHandler>
diff --git a/chromeos/services/bluetooth_config/initializer_impl.h b/chromeos/services/bluetooth_config/initializer_impl.h
index b78a9d2..27ec50f 100644
--- a/chromeos/services/bluetooth_config/initializer_impl.h
+++ b/chromeos/services/bluetooth_config/initializer_impl.h
@@ -32,10 +32,12 @@
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
       DeviceNameManager* device_name_manager) override;
+  std::unique_ptr<DiscoveredDevicesProvider> CreateDiscoveredDevicesProvider(
+      DeviceCache* device_cache) override;
   std::unique_ptr<DiscoverySessionManager> CreateDiscoverySessionManager(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-      DeviceCache* device_cache) override;
+      DiscoveredDevicesProvider* discovered_devices_provider) override;
   std::unique_ptr<DeviceOperationHandler> CreateDeviceOperationHandler(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) override;
diff --git a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc
index f803fca..8d1de5e 100644
--- a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc
+++ b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.cc
@@ -10,6 +10,7 @@
 #include "chromeos/services/bluetooth_config/fake_device_cache.h"
 #include "chromeos/services/bluetooth_config/fake_device_name_manager.h"
 #include "chromeos/services/bluetooth_config/fake_device_operation_handler.h"
+#include "chromeos/services/bluetooth_config/fake_discovered_devices_provider.h"
 #include "chromeos/services/bluetooth_config/fake_discovery_session_manager.h"
 #include "chromeos/services/bluetooth_config/in_process_instance.h"
 #include "device/bluetooth/bluetooth_adapter.h"
@@ -71,14 +72,23 @@
   return fake_device_cache;
 }
 
+std::unique_ptr<DiscoveredDevicesProvider>
+ScopedBluetoothConfigTestHelper::CreateDiscoveredDevicesProvider(
+    DeviceCache* device_cache) {
+  auto fake_discovered_devices_provider =
+      std::make_unique<FakeDiscoveredDevicesProvider>();
+  fake_discovered_devices_provider_ = fake_discovered_devices_provider.get();
+  return fake_discovered_devices_provider;
+}
+
 std::unique_ptr<DiscoverySessionManager>
 ScopedBluetoothConfigTestHelper::CreateDiscoverySessionManager(
     AdapterStateController* adapter_state_controller,
     scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-    DeviceCache* device_cache) {
+    DiscoveredDevicesProvider* discovered_devices_provider) {
   auto fake_discovery_session_manager =
-      std::make_unique<FakeDiscoverySessionManager>(adapter_state_controller,
-                                                    device_cache);
+      std::make_unique<FakeDiscoverySessionManager>(
+          adapter_state_controller, discovered_devices_provider);
   fake_discovery_session_manager_ = fake_discovery_session_manager.get();
   return fake_discovery_session_manager;
 }
diff --git a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h
index 3a7e566..a915632 100644
--- a/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h
+++ b/chromeos/services/bluetooth_config/scoped_bluetooth_config_test_helper.h
@@ -18,6 +18,7 @@
 class FakeDeviceCache;
 class FakeDeviceNameManager;
 class FakeDeviceOperationHandler;
+class FakeDiscoveredDevicesProvider;
 class FakeDiscoverySessionManager;
 
 // Test helper which provides access to fake implementations. This class
@@ -50,6 +51,10 @@
 
   FakeDeviceCache* fake_device_cache() { return fake_device_cache_; }
 
+  FakeDiscoveredDevicesProvider* fake_discovered_devices_provider() {
+    return fake_discovered_devices_provider_;
+  }
+
   FakeDiscoverySessionManager* fake_discovery_session_manager() {
     return fake_discovery_session_manager_;
   }
@@ -76,10 +81,12 @@
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
       DeviceNameManager* device_name_manager) override;
+  std::unique_ptr<DiscoveredDevicesProvider> CreateDiscoveredDevicesProvider(
+      DeviceCache* device_cache) override;
   std::unique_ptr<DiscoverySessionManager> CreateDiscoverySessionManager(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter,
-      DeviceCache* device_cache) override;
+      DiscoveredDevicesProvider* discovered_devices_provider) override;
   std::unique_ptr<DeviceOperationHandler> CreateDeviceOperationHandler(
       AdapterStateController* adapter_state_controller,
       scoped_refptr<device::BluetoothAdapter> bluetooth_adapter) override;
@@ -89,6 +96,7 @@
   FakeBluetoothPowerController* fake_bluetooth_power_controller_;
   FakeDeviceNameManager* fake_device_name_manager_;
   FakeDeviceCache* fake_device_cache_;
+  FakeDiscoveredDevicesProvider* fake_discovered_devices_provider_;
   FakeDiscoverySessionManager* fake_discovery_session_manager_;
   FakeDeviceOperationHandler* fake_device_operation_handler_;
   session_manager::SessionManager session_manager_;
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index 65f175d..c812fc5 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -262,6 +262,8 @@
     "payments/payments_util.cc",
     "payments/payments_util.h",
     "payments/risk_data_loader.h",
+    "payments/virtual_card_enrollment_manager.cc",
+    "payments/virtual_card_enrollment_manager.h",
     "payments/wait_for_signal_or_timeout.cc",
     "payments/wait_for_signal_or_timeout.h",
     "payments/webauthn_callback_types.h",
diff --git a/components/autofill/core/browser/payments/payments_client.cc b/components/autofill/core/browser/payments/payments_client.cc
index d75fe25..e196dcd 100644
--- a/components/autofill/core/browser/payments/payments_client.cc
+++ b/components/autofill/core/browser/payments/payments_client.cc
@@ -960,6 +960,11 @@
 PaymentsClient::SelectChallengeOptionRequestDetails::
     ~SelectChallengeOptionRequestDetails() = default;
 
+PaymentsClient::GetDetailsForEnrollmentResponseDetails::
+    GetDetailsForEnrollmentResponseDetails() = default;
+PaymentsClient::GetDetailsForEnrollmentResponseDetails::
+    ~GetDetailsForEnrollmentResponseDetails() = default;
+
 PaymentsClient::PaymentsClient(
     scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
     signin::IdentityManager* identity_manager,
diff --git a/components/autofill/core/browser/payments/payments_client.h b/components/autofill/core/browser/payments/payments_client.h
index e97f9e16..d69bfe5a 100644
--- a/components/autofill/core/browser/payments/payments_client.h
+++ b/components/autofill/core/browser/payments/payments_client.h
@@ -277,6 +277,26 @@
     LOCAL_CARD_MIGRATION_SETTINGS_PAGE,
   };
 
+  struct GetDetailsForEnrollmentResponseDetails {
+    GetDetailsForEnrollmentResponseDetails();
+    GetDetailsForEnrollmentResponseDetails(
+        const GetDetailsForEnrollmentResponseDetails&) = delete;
+    GetDetailsForEnrollmentResponseDetails& operator=(
+        const GetDetailsForEnrollmentResponseDetails&) = delete;
+    ~GetDetailsForEnrollmentResponseDetails();
+    // |vcn_context_token_| is used in the sequential Enroll call, where it
+    // allows the server to get the instrument id for this |vcn_context_token_|
+    // and link this specific GetDetailsForEnroll call with its corresponding
+    // enroll call.
+    std::string vcn_context_token_;
+    // Google's legal message lines in the virtual card enroll flow for this
+    // specific card based on |vcn_context_token_|.
+    LegalMessageLines google_legal_message_;
+    // The issuer's legal message lines in the virtual card enroll flow for this
+    // specific card based on |vcn_context_token_|.
+    LegalMessageLines issuer_legal_message_;
+  };
+
   // |url_loader_factory| is reference counted so it has no lifetime or
   // ownership requirements. |identity_manager| and |account_info_getter| must
   // all outlive |this|. Either delegate might be nullptr. |is_off_the_record|
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
new file mode 100644
index 0000000..4b8f074
--- /dev/null
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.cc
@@ -0,0 +1,53 @@
+// 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 "components/autofill/core/browser/payments/virtual_card_enrollment_manager.h"
+
+namespace autofill {
+
+VirtualCardEnrollmentFields::VirtualCardEnrollmentFields() = default;
+VirtualCardEnrollmentFields::~VirtualCardEnrollmentFields() = default;
+
+VirtualCardEnrollmentProcessState::VirtualCardEnrollmentProcessState() =
+    default;
+VirtualCardEnrollmentProcessState::~VirtualCardEnrollmentProcessState() =
+    default;
+
+VirtualCardEnrollmentManager::VirtualCardEnrollmentManager(
+    raw_ptr<AutofillClient> client,
+    const std::string& app_locale) {}
+
+VirtualCardEnrollmentManager::~VirtualCardEnrollmentManager() = default;
+
+void VirtualCardEnrollmentManager::OfferVirtualCardEnroll(
+    raw_ptr<CreditCard> credit_card,
+    VirtualCardEnrollmentFlow virtual_card_enrollment_flow) {}
+
+void VirtualCardEnrollmentManager::Unenroll(int64_t instrument_id) {}
+
+void OnRiskDataLoadedForVirtualCard(
+    std::unique_ptr<VirtualCardEnrollmentProcessState> state,
+    const std::string& risk_data) {}
+
+void GetDetailsForEnroll(
+    std::unique_ptr<VirtualCardEnrollmentProcessState> state) {}
+
+void OnDidGetDetailsForEnrollResponse(
+    std::unique_ptr<VirtualCardEnrollmentProcessState> state,
+    AutofillClient::PaymentsRpcResult result,
+    payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails
+        get_details_for_enrollment_response_fields) {}
+
+void VirtualCardEnrollmentManager::ShowVirtualCardEnrollmentBubble(
+    std::unique_ptr<VirtualCardEnrollmentProcessState> state) {}
+
+void VirtualCardEnrollmentManager::OnVirtualCardEnrollmentBubbleAccepted(
+    raw_ptr<CreditCard> credit_card) {}
+
+void VirtualCardEnrollmentManager::OnDidGetUpdateVirtualCardEnrollmentResponse(
+    CreditCard::VirtualCardEnrollmentState virtual_card_enrollment_state) {}
+
+void VirtualCardEnrollmentManager::OnVirtualCardEnrollmentBubbleCancelled() {}
+
+}  // namespace autofill
diff --git a/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
new file mode 100644
index 0000000..4817a42
--- /dev/null
+++ b/components/autofill/core/browser/payments/virtual_card_enrollment_manager.h
@@ -0,0 +1,146 @@
+// 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 COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_VIRTUAL_CARD_ENROLLMENT_MANAGER_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_VIRTUAL_CARD_ENROLLMENT_MANAGER_H_
+
+#include <string>
+
+#include "base/memory/raw_ptr.h"
+#include "components/autofill/core/browser/autofill_client.h"
+#include "components/autofill/core/browser/data_model/credit_card.h"
+#include "components/autofill/core/browser/payments/payments_client.h"
+
+namespace autofill {
+
+enum class VirtualCardEnrollmentFlow {
+  // Default value, should never be used.
+  kNone = 0,
+  // Offering VCN Enrollment after Upstream flow, i.e., saving a card to
+  // Google Payments.
+  kUpstream = 1,
+  // Offering VCN Enrollment after Downstream flow, i.e., unmasking a card
+  // from Google Payments.
+  kDownstream = 2,
+  // Offering VCN Enrollment from the payment methods settings page.
+  kSettingsPage = 3,
+  // Max value, needs to be updated every time a new enum is added.
+  kMaxValue = kSettingsPage,
+};
+
+// This struct is passed into the controller when we show the
+// VirtualCardEnrollmentBubble, and it lets the controller customize the
+// bubble based on the fields in this struct. For example, we will show
+// different last 4 digits of a credit card based on the |credit_card_| object
+// in this struct.
+struct VirtualCardEnrollmentFields {
+  VirtualCardEnrollmentFields();
+  VirtualCardEnrollmentFields(const VirtualCardEnrollmentFields&) = delete;
+  VirtualCardEnrollmentFields& operator=(const VirtualCardEnrollmentFields&) =
+      delete;
+  ~VirtualCardEnrollmentFields();
+  // Pointer to the credit card to enroll. The |credit_card_| object is owned
+  // by PersonalDataManager.
+  raw_ptr<CreditCard> credit_card_ = nullptr;
+  // Raw pointer to the image for the card art. The |card_art_image_| object is
+  // owned by PersonalDataManager.
+  raw_ptr<gfx::Image> card_art_image_ = nullptr;
+  // The legal message lines for the footer of the
+  // VirtualCardEnrollmentBubble.
+  LegalMessageLines legal_message_lines_;
+  // The flow for which the VirtualCardEnrollmentBubble will be shown.
+  VirtualCardEnrollmentFlow virtual_card_enroll_flow_ =
+      VirtualCardEnrollmentFlow::kNone;
+};
+
+struct VirtualCardEnrollmentProcessState {
+  VirtualCardEnrollmentProcessState();
+  VirtualCardEnrollmentProcessState(const VirtualCardEnrollmentProcessState&) =
+      delete;
+  VirtualCardEnrollmentProcessState& operator=(
+      const VirtualCardEnrollmentProcessState&) = delete;
+  ~VirtualCardEnrollmentProcessState();
+  // Only populated once the risk engine responded.
+  absl::optional<std::string> risk_data_;
+  // |credit_card_| and |virtual_card_enroll_flow_| are populated in the
+  // beginning of the virtual card enrollment flow, but the rest of the fields
+  // are only populated before showing the VirtualCardEnrollmentBubble.
+  VirtualCardEnrollmentFields virtual_card_enrollment_fields_;
+};
+
+// Owned by FormDataImporter. There is one instance of this class per tab. This
+// class manages the flow for enrolling and unenrolling in Virtual Card
+// Numbers.
+class VirtualCardEnrollmentManager {
+ public:
+  // The parameters should outlive the VirtualCardEnrollmentManager.
+  VirtualCardEnrollmentManager(raw_ptr<AutofillClient> client,
+                               const std::string& app_locale);
+  VirtualCardEnrollmentManager(const VirtualCardEnrollmentManager&) = delete;
+  VirtualCardEnrollmentManager& operator=(const VirtualCardEnrollmentManager&) =
+      delete;
+  ~VirtualCardEnrollmentManager();
+
+  // Starting point for the VCN enroll flow. The fields in |credit_card| will
+  // be used throughout the flow, such as for request fields as well as credit
+  // card specific fields for the bubble to display.
+  // |virtual_card_enrollment_flow| will be used by
+  // ShowVirtualCardEnrollBubble() to differentiate different bubbles based on
+  // the flow we are in.
+  void OfferVirtualCardEnroll(
+      raw_ptr<CreditCard> credit_card,
+      VirtualCardEnrollmentFlow virtual_card_enrollment_flow);
+
+  // Unenrolls the card mapped to the given |instrument_id|.
+  void Unenroll(int64_t instrument_id);
+
+ private:
+  // Called once the risk data is loaded. The |risk_data| will be used with
+  // |credit_card|'s |instrument_id_| field to make a GetDetailsForEnroll
+  // request, and |virtual_card_enroll_flow| will be passed down to when we show
+  // the bubble so that we show the correct bubble version.
+  void OnRiskDataLoadedForVirtualCard(
+      std::unique_ptr<VirtualCardEnrollmentProcessState> state,
+      const std::string& risk_data);
+
+  // Sends the GetDetailsForEnrollRequest using AutofillClient's
+  // |payments_client_|. |state|'s |risk_data| and |credit_card|'s
+  // |instrument_id| are the fields the server requires for the
+  // GetDetailsForEnrollRequest, and will be used by |payments_client_|.
+  // |state|'s |virtual_card_enrollment_fields_|'s
+  // |virtual_card_enrollment_flow| is passed here so that it can be forwarded
+  // to ShowVirtualCardEnrollBubble.
+  void GetDetailsForEnroll(
+      std::unique_ptr<VirtualCardEnrollmentProcessState> state);
+
+  // Handles the response from the GetDetailsForEnrollRequest.
+  void OnDidGetDetailsForEnrollResponse(
+      std::unique_ptr<VirtualCardEnrollmentProcessState> state,
+      AutofillClient::PaymentsRpcResult result,
+      const payments::PaymentsClient::GetDetailsForEnrollmentResponseDetails&
+          get_details_for_enrollment_response_fields);
+
+  // Shows the VirtualCardEnrollmentBubble. |state|'s
+  // |virtual_card_enrollment_fields| will contain all of the dynamic fields
+  // VirtualCardEnrollmentBubbleController needs to display the correct bubble.
+  void ShowVirtualCardEnrollmentBubble(
+      std::unique_ptr<VirtualCardEnrollmentProcessState> state);
+
+  // Uses AutofillClient's |payments_client_| to send the enroll request when
+  // the user accepts the bubble. |vcn_context_token_|, which
+  // should be set when we receive the GetDetailsForEnrollResponse, is used in
+  // the enroll request to enroll the correct card.
+  void OnVirtualCardEnrollmentBubbleAccepted(raw_ptr<CreditCard> credit_card);
+
+  // Handles the response from the Update Virtual Card Enrollment Request.
+  void OnDidGetUpdateVirtualCardEnrollmentResponse(
+      CreditCard::VirtualCardEnrollmentState virtual_card_enrollment_state);
+
+  // Cancels the entire Virtual Card Enrollment flow.
+  void OnVirtualCardEnrollmentBubbleCancelled();
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_VIRTUAL_CARD_ENROLLMENT_MANAGER_H_
diff --git a/components/exo/xdg_shell_surface.cc b/components/exo/xdg_shell_surface.cc
index 15d09c4..3793a37 100644
--- a/components/exo/xdg_shell_surface.cc
+++ b/components/exo/xdg_shell_surface.cc
@@ -6,6 +6,7 @@
 
 #include "ash/frame/non_client_frame_view_ash.h"
 #include "chromeos/ui/base/window_properties.h"
+#include "ui/aura/client/aura_constants.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
 #include "ui/gfx/geometry/rect.h"
@@ -34,6 +35,13 @@
       chromeos::kAutoMaximizeXdgShellEnabled);
   if (auto_maximize_enabled && ShouldAutoMaximize())
     params->show_state = ui::SHOW_STATE_MAXIMIZED;
+
+  // Show state should be overridden when set via window property.
+  if (params->init_properties_container.GetProperty(
+          aura::client::kShowStateKey)) {
+    params->show_state = params->init_properties_container.GetProperty(
+        aura::client::kShowStateKey);
+  }
 }
 
 bool XdgShellSurface::ShouldAutoMaximize() {
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index 06f274d..552b67a 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -341,7 +341,7 @@
     "//ui/gfx",
   ]
 
-  if (is_chromeos_lacros) {
+  if (is_chromeos) {
     deps += [ "//chromeos/crosapi/cpp:cpp" ]
   }
 
diff --git a/components/omnibox/browser/DEPS b/components/omnibox/browser/DEPS
index bdbd9e6..c7c1585 100644
--- a/components/omnibox/browser/DEPS
+++ b/components/omnibox/browser/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
   "+chromeos/crosapi/cpp/gurl_os_handler_utils.h",
+  "+chromeos/crosapi/cpp/lacros_startup_state.h",
   "+components/bookmarks/browser",
   "+components/bookmarks/test",
   "+components/browser_ui/widget/android/java",
diff --git a/components/omnibox/browser/autocomplete_input.cc b/components/omnibox/browser/autocomplete_input.cc
index 924d679..c26eb04 100644
--- a/components/omnibox/browser/autocomplete_input.cc
+++ b/components/omnibox/browser/autocomplete_input.cc
@@ -26,9 +26,12 @@
 #include "url/url_canon_ip.h"
 #include "url/url_util.h"
 
-#if BUILDFLAG(IS_CHROMEOS_LACROS)
+#if defined(OS_CHROMEOS)
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "chromeos/crosapi/cpp/lacros_startup_state.h"  // nogncheck
+#endif  // BUILDFLAG(IS_CHROMEOS_ASH)
 #include "chromeos/crosapi/cpp/gurl_os_handler_utils.h"  // nogncheck
-#endif
+#endif                                                   // defined(OS_CHROMEOS)
 
 namespace {
 
@@ -256,8 +259,17 @@
   if (!canonicalized_url->is_valid())
     return metrics::OmniboxInputType::QUERY;
 
+#if defined(OS_CHROMEOS)
+  const bool is_lacros_or_lacros_is_primary =
 #if BUILDFLAG(IS_CHROMEOS_LACROS)
-  if (crosapi::gurl_os_handler_utils::IsAshOsAsciiScheme(parsed_scheme_utf8)) {
+      true;
+#else
+      // ChromeOS's launcher is using the omnibox from Ash. As such we have to
+      // allow Ash to use the os scheme if Lacros is the primary browser.
+      crosapi::lacros_startup_state::IsLacrosPrimaryEnabled();
+#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)
+  if (is_lacros_or_lacros_is_primary &&
+      crosapi::gurl_os_handler_utils::IsAshOsAsciiScheme(parsed_scheme_utf8)) {
     // Lacros and Ash have a different set of internal chrome:// pages.
     // However - once Lacros is the primary browser, the Ash browser cannot be
     // reached anymore and many internal status / information / ... pages
@@ -266,7 +278,7 @@
     // making them accessible again.
     return metrics::OmniboxInputType::URL;
   }
-#endif
+#endif  // defined(OS_CHROMEOS)
 
   if (base::LowerCaseEqualsASCII(parsed_scheme_utf8, url::kFileScheme)) {
     // A user might or might not type a scheme when entering a file URL.  In
diff --git a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
index d80fd0b..f95f8c3 100644
--- a/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
+++ b/components/password_manager/core/browser/password_store_backend_migration_decorator.cc
@@ -34,7 +34,7 @@
   DCHECK(built_in_backend_);
   DCHECK(android_backend_);
   active_backend_ = std::make_unique<PasswordStoreProxyBackend>(
-      built_in_backend_.get(), android_backend_.get(), prefs_,
+      built_in_backend_.get(), android_backend_.get(),
       is_syncing_passwords_callback_);
 }
 
diff --git a/components/password_manager/core/browser/password_store_proxy_backend.cc b/components/password_manager/core/browser/password_store_proxy_backend.cc
index 1853166..7e964a5 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend.cc
+++ b/components/password_manager/core/browser/password_store_proxy_backend.cc
@@ -18,32 +18,12 @@
 #include "base/strings/strcat.h"
 #include "components/password_manager/core/browser/field_info_table.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_service.h"
 #include "components/sync/model/proxy_model_type_controller_delegate.h"
 
 namespace password_manager {
 
 namespace {
 
-bool ShouldExecuteModifyOperationsOnShadowBackend(PrefService* prefs,
-                                                  bool is_syncing) {
-  if (!base::FeatureList::IsEnabled(
-          features::kUnifiedPasswordManagerShadowWriteOperationsAndroid)) {
-    return false;
-  }
-  if (is_syncing)
-    return false;
-  if (features::kMigrationVersion.Get() >
-      prefs->GetInteger(
-          prefs::kCurrentMigrationVersionToGoogleMobileServices)) {
-    // If initial migration isn't completed yet, we shouldn't modify the shadow
-    // backend.
-    return false;
-  }
-  return true;
-}
-
 using MethodName = base::StrongAlias<struct MethodNameTag, std::string>;
 
 struct LoginsResultOrErrorImpl {
@@ -73,33 +53,6 @@
   }
 };
 
-struct PasswordStoreChangeListImpl {
-  using ResultType = absl::optional<PasswordStoreChangeList>;
-  using ElementsType = PasswordStoreChangeList;
-
-  static PasswordStoreChangeList* GetElements(
-      absl::optional<PasswordStoreChangeList>& changelist) {
-    return changelist.has_value() ? &changelist.value() : nullptr;
-  }
-
-  static PasswordStoreChange Clone(const PasswordStoreChange& change) {
-    return change;
-  }
-
-  static bool IsLess(const PasswordStoreChange& lhs,
-                     const PasswordStoreChange& rhs) {
-    return std::forward_as_tuple(PasswordFormUniqueKey(lhs.form()),
-                                 lhs.type()) <
-           std::forward_as_tuple(PasswordFormUniqueKey(rhs.form()), rhs.type());
-  }
-
-  static bool HaveInconsistentPasswords(const PasswordStoreChange& lhs,
-                                        const PasswordStoreChange& rhs) {
-    // We never consider PasswordStoreChange having inconsistent passwords.
-    return false;
-  }
-};
-
 void InvokeCallbackWithCombinedStatus(base::OnceCallback<void(bool)> completion,
                                       std::vector<bool> statuses) {
   std::move(completion).Run(base::ranges::all_of(statuses, base::identity()));
@@ -238,11 +191,9 @@
 PasswordStoreProxyBackend::PasswordStoreProxyBackend(
     PasswordStoreBackend* main_backend,
     PasswordStoreBackend* shadow_backend,
-    PrefService* prefs,
     base::RepeatingCallback<bool()> is_syncing_passwords_callback)
     : main_backend_(main_backend),
       shadow_backend_(shadow_backend),
-      prefs_(prefs),
       is_syncing_passwords_callback_(std::move(is_syncing_passwords_callback)) {
 }
 
@@ -276,9 +227,10 @@
 }
 
 void PasswordStoreProxyBackend::GetAllLoginsAsync(LoginsOrErrorReply callback) {
-  auto handler = base::MakeRefCounted<
-      ShadowTrafficMetricsRecorder<LoginsResultOrErrorImpl>>(
-      MethodName("GetAllLoginsAsync"));
+  scoped_refptr<ShadowTrafficMetricsRecorder<LoginsResultOrErrorImpl>> handler =
+      base::MakeRefCounted<
+          ShadowTrafficMetricsRecorder<LoginsResultOrErrorImpl>>(
+          MethodName("GetAllLoginsAsync"));
   main_backend_->GetAllLoginsAsync(
       base::BindOnce(&ShadowTrafficMetricsRecorder<
                          LoginsResultOrErrorImpl>::RecordMainResult,
@@ -313,23 +265,8 @@
 void PasswordStoreProxyBackend::AddLoginAsync(
     const PasswordForm& form,
     PasswordStoreChangeListReply callback) {
-  auto handler = base::MakeRefCounted<
-      ShadowTrafficMetricsRecorder<PasswordStoreChangeListImpl>>(
-      MethodName("AddLoginAsync"));
-
-  main_backend_->AddLoginAsync(
-      form, base::BindOnce(&ShadowTrafficMetricsRecorder<
-                               PasswordStoreChangeListImpl>::RecordMainResult,
-                           handler)
-                .Then(std::move(callback)));
-  if (ShouldExecuteModifyOperationsOnShadowBackend(
-          prefs_, is_syncing_passwords_callback_.Run())) {
-    shadow_backend_->AddLoginAsync(
-        form,
-        base::BindOnce(&ShadowTrafficMetricsRecorder<
-                           PasswordStoreChangeListImpl>::RecordShadowResult,
-                       handler));
-  }
+  main_backend_->AddLoginAsync(form, std::move(callback));
+  // TODO(crbug.com/1229655): Request shadow_backend_ and compare results.
 }
 
 void PasswordStoreProxyBackend::UpdateLoginAsync(
diff --git a/components/password_manager/core/browser/password_store_proxy_backend.h b/components/password_manager/core/browser/password_store_proxy_backend.h
index 0044545..0724074e 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend.h
+++ b/components/password_manager/core/browser/password_store_proxy_backend.h
@@ -12,8 +12,6 @@
 #include "base/memory/weak_ptr.h"
 #include "components/password_manager/core/browser/password_store_backend.h"
 
-class PrefService;
-
 namespace password_manager {
 
 // This backend forwards requests to two backends in order to compare and record
@@ -26,7 +24,6 @@
   PasswordStoreProxyBackend(
       PasswordStoreBackend* main_backend,
       PasswordStoreBackend* shadow_backend,
-      PrefService* prefs,
       base::RepeatingCallback<bool()> is_syncing_passwords_callback);
   PasswordStoreProxyBackend(const PasswordStoreProxyBackend&) = delete;
   PasswordStoreProxyBackend(PasswordStoreProxyBackend&&) = delete;
@@ -74,7 +71,6 @@
 
   const raw_ptr<PasswordStoreBackend> main_backend_;
   const raw_ptr<PasswordStoreBackend> shadow_backend_;
-  raw_ptr<PrefService> const prefs_ = nullptr;
   base::RepeatingCallback<bool()> is_syncing_passwords_callback_;
   base::WeakPtrFactory<PasswordStoreProxyBackend> weak_ptr_factory_{this};
 };
diff --git a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
index b03f003a..cd6e7fbb 100644
--- a/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
+++ b/components/password_manager/core/browser/password_store_proxy_backend_unittest.cc
@@ -18,9 +18,6 @@
 #include "components/password_manager/core/browser/password_manager_test_utils.h"
 #include "components/password_manager/core/browser/password_store_change.h"
 #include "components/password_manager/core/common/password_manager_features.h"
-#include "components/password_manager/core/common/password_manager_pref_names.h"
-#include "components/prefs/pref_registry_simple.h"
-#include "components/prefs/testing_pref_service.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "url/gurl.h"
@@ -71,17 +68,10 @@
  protected:
   PasswordStoreProxyBackendTest() {
     proxy_backend_ = std::make_unique<PasswordStoreProxyBackend>(
-        &main_backend_, &shadow_backend_, &prefs_,
-        is_syncing_passwords_callback_.Get());
+        &main_backend_, &shadow_backend_, is_syncing_passwords_callback_.Get());
 
-    feature_list_.InitWithFeatures(
-        /*enabled_features=*/
-        {features::kUnifiedPasswordManagerShadowAndroid,
-         features::kUnifiedPasswordManagerShadowWriteOperationsAndroid},
-        /*disabled_features=*/{});
-
-    prefs_.registry()->RegisterIntegerPref(
-        prefs::kCurrentMigrationVersionToGoogleMobileServices, 0);
+    feature_list_.InitAndEnableFeature(
+        features::kUnifiedPasswordManagerShadowAndroid);
   }
 
   void TearDown() override {
@@ -95,14 +85,12 @@
   PasswordStoreBackend& proxy_backend() { return *proxy_backend_; }
   MockPasswordStoreBackend& main_backend() { return main_backend_; }
   MockPasswordStoreBackend& shadow_backend() { return shadow_backend_; }
-  TestingPrefServiceSimple* prefs() { return &prefs_; }
 
   base::MockCallback<base::RepeatingCallback<bool(void)>>
       is_syncing_passwords_callback_;
 
  private:
   base::test::ScopedFeatureList feature_list_;
-  TestingPrefServiceSimple prefs_;
   std::unique_ptr<PasswordStoreProxyBackend> proxy_backend_;
   StrictMock<MockPasswordStoreBackend> main_backend_;
   StrictMock<MockPasswordStoreBackend> shadow_backend_;
@@ -327,97 +315,6 @@
   histogram_tester.ExpectTotalCount(prefix + "InconsistentPasswords.Rel", 0);
 }
 
-TEST_F(PasswordStoreProxyBackendTest, NoShadowAddLoginsAsyncWhenSyncEnabled) {
-  EXPECT_CALL(is_syncing_passwords_callback_, Run).WillRepeatedly(Return(true));
-
-  EXPECT_CALL(main_backend(), AddLoginAsync);
-  EXPECT_CALL(shadow_backend(), AddLoginAsync).Times(0);
-  proxy_backend().AddLoginAsync(CreateTestForm(),
-                                /*callback=*/base::DoNothing());
-}
-
-TEST_F(PasswordStoreProxyBackendTest,
-       NoShadowAddLoginsAsyncWhenSyncDisabledAndInitialMigrationIncomplete) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
-      /*disabled_features=*/{});
-  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 1);
-
-  EXPECT_CALL(is_syncing_passwords_callback_, Run)
-      .WillRepeatedly(Return(false));
-
-  EXPECT_CALL(main_backend(), AddLoginAsync);
-  EXPECT_CALL(shadow_backend(), AddLoginAsync).Times(0);
-  proxy_backend().AddLoginAsync(CreateTestForm(),
-                                /*callback=*/base::DoNothing());
-}
-
-TEST_F(PasswordStoreProxyBackendTest,
-       ShadowAddLoginsAsyncWhenSyncDisabledAndInitialMigrationComplete) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
-      /*disabled_features=*/{});
-  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
-
-  EXPECT_CALL(is_syncing_passwords_callback_, Run)
-      .WillRepeatedly(Return(false));
-
-  EXPECT_CALL(main_backend(), AddLoginAsync);
-  EXPECT_CALL(shadow_backend(), AddLoginAsync);
-  proxy_backend().AddLoginAsync(CreateTestForm(),
-                                /*callback=*/base::DoNothing());
-}
-
-TEST_F(PasswordStoreProxyBackendTest, ShadowAddLoginsAsyncBasicMetricsTesting) {
-  base::HistogramTester histogram_tester;
-  // Set the prefs such that no initial migration is required to allow shadow
-  // write operations.
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitWithFeaturesAndParameters(
-      /*enabled_features=*/{{features::kUnifiedPasswordManagerMigration,
-                             {{"migration_version", "2"}}}},
-      /*disabled_features=*/{});
-  prefs()->SetInteger(prefs::kCurrentMigrationVersionToGoogleMobileServices, 2);
-  // Shadow write operations run only for non-syncing users.
-  EXPECT_CALL(is_syncing_passwords_callback_, Run)
-      .WillRepeatedly(Return(false));
-
-  PasswordForm test_form = CreateTestForm();
-
-  PasswordStoreChangeList main_backend_changelist;
-  main_backend_changelist.emplace_back(PasswordStoreChange::ADD, test_form);
-
-  PasswordStoreChangeList shadow_backend_changelist;
-  shadow_backend_changelist.emplace_back(PasswordStoreChange::UPDATE,
-                                         test_form);
-
-  EXPECT_CALL(main_backend(), AddLoginAsync)
-      .WillOnce(WithArg<1>(Invoke(
-          [&main_backend_changelist](PasswordStoreChangeListReply reply)
-              -> void { std::move(reply).Run(main_backend_changelist); })));
-
-  EXPECT_CALL(shadow_backend(), AddLoginAsync)
-      .WillOnce(WithArg<1>(Invoke(
-          [&shadow_backend_changelist](PasswordStoreChangeListReply reply)
-              -> void { std::move(reply).Run(shadow_backend_changelist); })));
-
-  proxy_backend().AddLoginAsync(test_form,
-                                /*callback=*/base::DoNothing());
-
-  std::string prefix =
-      "PasswordManager.PasswordStoreProxyBackend.AddLoginAsync.";
-
-  histogram_tester.ExpectUniqueSample(prefix + "Diff.Abs", 2, 1);
-  histogram_tester.ExpectUniqueSample(prefix + "MainMinusShadow.Abs", 1, 1);
-  histogram_tester.ExpectUniqueSample(prefix + "ShadowMinusMain.Abs", 1, 1);
-  histogram_tester.ExpectUniqueSample(prefix + "InconsistentPasswords.Abs", 0,
-                                      1);
-}
-
 // Holds the main and shadow backend's logins and the expected number of common
 // and different logins.
 struct LoginsMetricsParam {
diff --git a/components/password_manager/core/common/password_manager_features.cc b/components/password_manager/core/common/password_manager_features.cc
index 7102bae..cd5b413a 100644
--- a/components/password_manager/core/common/password_manager_features.cc
+++ b/components/password_manager/core/common/password_manager_features.cc
@@ -161,12 +161,6 @@
 const base::Feature kUnifiedPasswordManagerShadowAndroid{
     "UnifiedPasswordManagerShadowAndroid", base::FEATURE_DISABLED_BY_DEFAULT};
 
-// Similar to kUnifiedPasswordManagerShadowAndroid but send modify operations
-// instead of read operations.Relevant only for non-sync'ing users.
-const base::Feature kUnifiedPasswordManagerShadowWriteOperationsAndroid{
-    "UnifiedPasswordManagerShadowWriteOperationsAndroid",
-    base::FEATURE_DISABLED_BY_DEFAULT};
-
 // If enabled, the built-in sync functionality in PasswordSyncBridge becomes
 // unused, meaning that SyncService/SyncEngine will no longer download or
 // upload changes to/from the Sync server. Instead, an external Android-specific
diff --git a/components/password_manager/core/common/password_manager_features.h b/components/password_manager/core/common/password_manager_features.h
index 78d56ec4..8d183d8e 100644
--- a/components/password_manager/core/common/password_manager_features.h
+++ b/components/password_manager/core/common/password_manager_features.h
@@ -47,7 +47,6 @@
 extern const base::Feature kUnifiedPasswordManagerAndroid;
 extern const base::Feature kUnifiedPasswordManagerMigration;
 extern const base::Feature kUnifiedPasswordManagerShadowAndroid;
-extern const base::Feature kUnifiedPasswordManagerShadowWriteOperationsAndroid;
 extern const base::Feature kUnifiedPasswordManagerSyncUsingAndroidBackendOnly;
 
 #endif
diff --git a/components/policy/core/browser/signin/user_cloud_signin_restriction_policy_fetcher.cc b/components/policy/core/browser/signin/user_cloud_signin_restriction_policy_fetcher.cc
index 06de577..f161255 100644
--- a/components/policy/core/browser/signin/user_cloud_signin_restriction_policy_fetcher.cc
+++ b/components/policy/core/browser/signin/user_cloud_signin_restriction_policy_fetcher.cc
@@ -7,6 +7,7 @@
 #include <set>
 
 #include "base/command_line.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/strings/stringprintf.h"
 #include "components/policy/core/browser/browser_policy_connector.h"
 #include "components/policy/core/common/cloud/cloud_policy_client_registration_helper.h"
@@ -157,14 +158,24 @@
   std::string restriction;
   std::unique_ptr<network::SimpleURLLoader> url_loader = std::move(url_loader_);
 
-  // TODO (crbug/1261474): Add metrics for the failure rate.
   GoogleServiceAuthError error = GoogleServiceAuthError::AuthErrorNone();
+  absl::optional<int> response_code;
+  if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers)
+    response_code = url_loader->ResponseInfo()->headers->response_code();
+
+  if (response_code)
+    base::UmaHistogramSparse(
+        "Enterprise.ProfileSeparation.DasherPolicyFetch.HttpResponse",
+        response_code.value());
+
+  base::UmaHistogramSparse(
+      "Enterprise.ProfileSeparation.DasherPolicyFetch.NetworkError",
+      url_loader->NetError());
   if (url_loader->NetError() != net::OK) {
-    if (url_loader->ResponseInfo() && url_loader->ResponseInfo()->headers) {
-      int response_code = url_loader->ResponseInfo()->headers->response_code();
+    if (response_code) {
       LOG(WARNING)
           << "ManagedAccountsSigninRestriction request failed with HTTP code: "
-          << response_code;
+          << response_code.value();
     } else {
       error =
           GoogleServiceAuthError::FromConnectionError(url_loader->NetError());
diff --git a/components/policy/core/common/cloud/cloud_policy_client.cc b/components/policy/core/common/cloud/cloud_policy_client.cc
index a0dd44c..1a9380f 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client.cc
@@ -618,16 +618,14 @@
           base::BindOnce(&CloudPolicyClient::OnReportUploadCompleted,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 
-  em::DeviceManagementRequest* request = config->request();
-  request->set_allocated_chrome_desktop_report_request(
+  config->request()->set_allocated_chrome_desktop_report_request(
       chrome_desktop_report.release());
 
   request_jobs_.push_back(service_->CreateJob(std::move(config)));
 }
 
 void CloudPolicyClient::UploadChromeOsUserReport(
-    std::unique_ptr<enterprise_management::ChromeOsUserReportRequest>
-        chrome_os_user_report,
+    std::unique_ptr<em::ChromeOsUserReportRequest> chrome_os_user_report,
     CloudPolicyClient::StatusCallback callback) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   CHECK(is_registered());
@@ -642,13 +640,34 @@
           base::BindOnce(&CloudPolicyClient::OnReportUploadCompleted,
                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
 
-  em::DeviceManagementRequest* request = config->request();
-  request->set_allocated_chrome_os_user_report_request(
+  config->request()->set_allocated_chrome_os_user_report_request(
       chrome_os_user_report.release());
 
   request_jobs_.push_back(service_->CreateJob(std::move(config)));
 }
 
+void CloudPolicyClient::UploadChromeProfileReport(
+    std::unique_ptr<em::ChromeProfileReportRequest> chrome_profile_report,
+    CloudPolicyClient::StatusCallback callback) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  CHECK(is_registered());
+  DCHECK(chrome_profile_report);
+
+  std::unique_ptr<DMServerJobConfiguration> config =
+      std::make_unique<DMServerJobConfiguration>(
+          DeviceManagementService::JobConfiguration::TYPE_CHROME_PROFILE_REPORT,
+          this,
+          /*critical=*/false, DMAuth::FromDMToken(dm_token_),
+          /*oauth_token=*/absl::nullopt,
+          base::BindOnce(&CloudPolicyClient::OnReportUploadCompleted,
+                         weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
+
+  config->request()->set_allocated_chrome_profile_report_request(
+      chrome_profile_report.release());
+
+  request_jobs_.push_back(service_->CreateJob(std::move(config)));
+}
+
 void CloudPolicyClient::UploadSecurityEventReport(
     content::BrowserContext* context,
     bool include_device_info,
diff --git a/components/policy/core/common/cloud/cloud_policy_client.h b/components/policy/core/common/cloud/cloud_policy_client.h
index 20c5ef4..df9545a 100644
--- a/components/policy/core/common/cloud/cloud_policy_client.h
+++ b/components/policy/core/common/cloud/cloud_policy_client.h
@@ -335,14 +335,22 @@
           chrome_desktop_report,
       StatusCallback callback);
 
-  // Uploads Chrome OS User report to the server. The user dm token must be set
-  // properly. |chrome_os_user_report| will be included in the upload request.
-  // The |callback| will be called when the operation completes.
+  // Uploads Chrome OS User report to the server. The user's DM token must be
+  // set. |chrome_os_user_report| will be included in the upload request. The
+  // |callback| will be called when the operation completes.
   virtual void UploadChromeOsUserReport(
       std::unique_ptr<enterprise_management::ChromeOsUserReportRequest>
           chrome_os_user_report,
       StatusCallback callback);
 
+  // Uploads Chrome profile report to the server. The user's DM token must be
+  // set. |chrome_profile_report| will be included in the upload request. The
+  // |callback| will be called when the operation completes.
+  virtual void UploadChromeProfileReport(
+      std::unique_ptr<enterprise_management::ChromeProfileReportRequest>
+          chrome_profile_report,
+      StatusCallback callback);
+
   // Uploads a report containing enterprise connectors real-time security
   // events for |context|. As above, the client must be in a registered state.
   // If |include_device_info| is true, information specific to the device such
diff --git a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
index 13e60b2..ace5acf 100644
--- a/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
+++ b/components/policy/core/common/cloud/cloud_policy_client_unittest.cc
@@ -85,42 +85,43 @@
 
 namespace {
 
-const char kClientID[] = "fake-client-id";
-const char kMachineID[] = "fake-machine-id";
-const char kMachineModel[] = "fake-machine-model";
-const char kBrandCode[] = "fake-brand-code";
-const char kAttestedDeviceId[] = "fake-attested-device-id";
-const char kEthernetMacAddress[] = "fake-ethernet-mac-address";
-const char kDockMacAddress[] = "fake-dock-mac-address";
-const char kManufactureDate[] = "fake-manufacture-date";
-const char kOAuthToken[] = "fake-oauth-token";
-const char kDMToken[] = "fake-dm-token";
-const char kDeviceDMToken[] = "fake-device-dm-token";
-const char kMachineCertificate[] = "fake-machine-certificate";
-const char kEnrollmentCertificate[] = "fake-enrollment-certificate";
-const char kEnrollmentId[] = "fake-enrollment-id";
+constexpr char kClientID[] = "fake-client-id";
+constexpr char kMachineID[] = "fake-machine-id";
+constexpr char kMachineModel[] = "fake-machine-model";
+constexpr char kBrandCode[] = "fake-brand-code";
+constexpr char kAttestedDeviceId[] = "fake-attested-device-id";
+constexpr char kEthernetMacAddress[] = "fake-ethernet-mac-address";
+constexpr char kDockMacAddress[] = "fake-dock-mac-address";
+constexpr char kManufactureDate[] = "fake-manufacture-date";
+constexpr char kOAuthToken[] = "fake-oauth-token";
+constexpr char kDMToken[] = "fake-dm-token";
+constexpr char kDeviceDMToken[] = "fake-device-dm-token";
+constexpr char kMachineCertificate[] = "fake-machine-certificate";
+constexpr char kEnrollmentCertificate[] = "fake-enrollment-certificate";
+constexpr char kEnrollmentId[] = "fake-enrollment-id";
+constexpr char kOsName[] = "fake-os-name";
 
 #if defined(OS_WIN) || defined(OS_APPLE) || \
     (defined(OS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
-const char kEnrollmentToken[] = "enrollment_token";
+constexpr char kEnrollmentToken[] = "enrollment_token";
 #endif
 
-const char kRequisition[] = "fake-requisition";
-const char kStateKey[] = "fake-state-key";
-const char kPayload[] = "input_payload";
-const char kResultPayload[] = "output_payload";
-const char kAssetId[] = "fake-asset-id";
-const char kLocation[] = "fake-location";
-const char kGcmID[] = "fake-gcm-id";
-const char kPolicyToken[] = "fake-policy-token";
-const char kPolicyName[] = "fake-policy-name";
-const char kValueValidationMessage[] = "fake-value-validation-message";
-const char kRobotAuthCode[] = "fake-robot-auth-code";
-const char kApiAuthScope[] = "fake-api-auth-scope";
+constexpr char kRequisition[] = "fake-requisition";
+constexpr char kStateKey[] = "fake-state-key";
+constexpr char kPayload[] = "input_payload";
+constexpr char kResultPayload[] = "output_payload";
+constexpr char kAssetId[] = "fake-asset-id";
+constexpr char kLocation[] = "fake-location";
+constexpr char kGcmID[] = "fake-gcm-id";
+constexpr char kPolicyToken[] = "fake-policy-token";
+constexpr char kPolicyName[] = "fake-policy-name";
+constexpr char kValueValidationMessage[] = "fake-value-validation-message";
+constexpr char kRobotAuthCode[] = "fake-robot-auth-code";
+constexpr char kApiAuthScope[] = "fake-api-auth-scope";
 
-const int64_t kAgeOfCommand = 123123123;
-const int64_t kLastCommandId = 123456789;
-const int64_t kTimestamp = 987654321;
+constexpr int64_t kAgeOfCommand = 123123123;
+constexpr int64_t kLastCommandId = 123456789;
+constexpr int64_t kTimestamp = 987654321;
 
 MATCHER_P(MatchProto, expected, "matches protobuf") {
   return arg.SerializePartialAsString() == expected.SerializePartialAsString();
@@ -1658,6 +1659,34 @@
   EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
 }
 
+TEST_F(CloudPolicyClientTest, UploadChromeProfileReport) {
+  RegisterClient();
+
+  em::DeviceManagementRequest device_managment_request;
+  device_managment_request.mutable_chrome_profile_report_request()
+      ->mutable_os_report()
+      ->set_name(kOsName);
+
+  ExpectAndCaptureJob(GetEmptyResponse());
+  EXPECT_CALL(callback_observer_, OnCallbackComplete(true)).Times(1);
+  CloudPolicyClient::StatusCallback callback =
+      base::BindOnce(&MockStatusCallbackObserver::OnCallbackComplete,
+                     base::Unretained(&callback_observer_));
+  auto chrome_profile_report =
+      std::make_unique<em::ChromeProfileReportRequest>();
+  chrome_profile_report->mutable_os_report()->set_name(kOsName);
+  client_->UploadChromeProfileReport(std::move(chrome_profile_report),
+                                     std::move(callback));
+  base::RunLoop().RunUntilIdle();
+  EXPECT_EQ(
+      DeviceManagementService::JobConfiguration::TYPE_CHROME_PROFILE_REPORT,
+      job_type_);
+  EXPECT_EQ(auth_data_, DMAuth::FromDMToken(kDMToken));
+  EXPECT_EQ(job_request_.SerializePartialAsString(),
+            device_managment_request.SerializePartialAsString());
+  EXPECT_EQ(DM_STATUS_SUCCESS, client_->status());
+}
+
 // A helper class to test all em::DeviceRegisterRequest::PsmExecutionResult enum
 // values.
 class CloudPolicyClientRegisterWithPsmParamsTest
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.cc b/components/policy/core/common/cloud/cloud_policy_constants.cc
index e8e7503..c454dc0 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.cc
+++ b/components/policy/core/common/cloud/cloud_policy_constants.cc
@@ -70,6 +70,7 @@
     "policy_validation_report";
 const char kValueRequestPublicSamlUser[] = "public_saml_user_request";
 const char kValueRequestCertProvisioningRequest[] = "client_cert_provisioning";
+const char kValueRequestChromeProfileReport[] = "chrome_profile_report";
 
 const char kChromeDevicePolicyType[] = "google/chromeos/device";
 #if BUILDFLAG(IS_CHROMEOS_ASH)
diff --git a/components/policy/core/common/cloud/cloud_policy_constants.h b/components/policy/core/common/cloud/cloud_policy_constants.h
index 794f28b..8ca4a52 100644
--- a/components/policy/core/common/cloud/cloud_policy_constants.h
+++ b/components/policy/core/common/cloud/cloud_policy_constants.h
@@ -63,6 +63,7 @@
 POLICY_EXPORT extern const char kValueRequestPublicSamlUser[];
 POLICY_EXPORT extern const char kValueRequestChromeOsUserReport[];
 POLICY_EXPORT extern const char kValueRequestCertProvisioningRequest[];
+POLICY_EXPORT extern const char kValueRequestChromeProfileReport[];
 
 // Policy type strings for the policy_type field in PolicyFetchRequest.
 POLICY_EXPORT extern const char kChromeDevicePolicyType[];
diff --git a/components/policy/core/common/cloud/device_management_service.cc b/components/policy/core/common/cloud/device_management_service.cc
index 983ec3df..093d84b 100644
--- a/components/policy/core/common/cloud/device_management_service.cc
+++ b/components/policy/core/common/cloud/device_management_service.cc
@@ -203,6 +203,8 @@
     case DeviceManagementService::JobConfiguration::
         TYPE_BROWSER_UPLOAD_PUBLIC_KEY:
       return "BrowserUploadPublicKey";
+    case DeviceManagementService::JobConfiguration::TYPE_CHROME_PROFILE_REPORT:
+      return "ChromeProfileReport";
   }
   NOTREACHED() << "Invalid job type " << type;
   return "";
diff --git a/components/policy/core/common/cloud/device_management_service.h b/components/policy/core/common/cloud/device_management_service.h
index 9f9b331..7b8aef595 100644
--- a/components/policy/core/common/cloud/device_management_service.h
+++ b/components/policy/core/common/cloud/device_management_service.h
@@ -221,6 +221,7 @@
       TYPE_CHECK_USER_ACCOUNT = 28,
       TYPE_UPLOAD_EUICC_INFO = 29,
       TYPE_BROWSER_UPLOAD_PUBLIC_KEY = 30,
+      TYPE_CHROME_PROFILE_REPORT = 31,
     };
 
     // The set of HTTP query parameters of the request.
diff --git a/components/policy/core/common/cloud/dmserver_job_configurations.cc b/components/policy/core/common/cloud/dmserver_job_configurations.cc
index eea777c..08c65c0c 100644
--- a/components/policy/core/common/cloud/dmserver_job_configurations.cc
+++ b/components/policy/core/common/cloud/dmserver_job_configurations.cc
@@ -7,6 +7,7 @@
 #include "base/strings/string_number_conversions.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/cloud/device_management_service.h"
 #include "net/base/url_util.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
 #include "url/gurl.h"
@@ -95,6 +96,8 @@
       break;
     case DeviceManagementService::JobConfiguration::TYPE_UPLOAD_EUICC_INFO:
       return dm_protocol::kValueRequestUploadEuiccInfo;
+    case DeviceManagementService::JobConfiguration::TYPE_CHROME_PROFILE_REPORT:
+      return dm_protocol::kValueRequestChromeProfileReport;
   }
   NOTREACHED() << "Invalid job type " << type;
   return "";
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index 609a77b..241cec9 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -4243,7 +4243,10 @@
   // Request to upload info about eSIM provisioning.
   optional UploadEuiccInfoRequest upload_euicc_info_request = 39;
 
-  // Next id: 40.
+  // Request to upload profile usage information.
+  optional ChromeProfileReportRequest chrome_profile_report_request = 40;
+
+  // Next id: 41.
 }
 
 // Response from server to device.
@@ -4386,7 +4389,10 @@
   // Response to EUICC info upload request.
   optional UploadEuiccInfoResponse upload_euicc_info_response = 37;
 
-  // Next id: 38.
+  // Response to a Chrome Profile report request.
+  optional ChromeProfileReportResponse chrome_profile_report_response = 38;
+
+  // Next id: 39.
 }
 
 // Device State Information stored in the server is retrieval at
diff --git a/components/reporting/metrics/fake_reporting_settings.cc b/components/reporting/metrics/fake_reporting_settings.cc
index 0ad9d82..ce24943 100644
--- a/components/reporting/metrics/fake_reporting_settings.cc
+++ b/components/reporting/metrics/fake_reporting_settings.cc
@@ -9,6 +9,8 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 
 namespace reporting {
+namespace test {
+
 FakeReportingSettings::FakeReportingSettings() = default;
 
 FakeReportingSettings::~FakeReportingSettings() = default;
@@ -74,4 +76,5 @@
                                                    run_loop.QuitClosure());
   run_loop.Run();
 }
+}  // namespace test
 }  // namespace reporting
diff --git a/components/reporting/metrics/fake_reporting_settings.h b/components/reporting/metrics/fake_reporting_settings.h
index 0a32049..3927f69 100644
--- a/components/reporting/metrics/fake_reporting_settings.h
+++ b/components/reporting/metrics/fake_reporting_settings.h
@@ -15,6 +15,7 @@
 #include "components/reporting/metrics/reporting_settings.h"
 
 namespace reporting {
+namespace test {
 
 // Fake reporting settings for testing.
 class FakeReportingSettings : public ReportingSettings {
@@ -52,6 +53,7 @@
 
   bool is_trusted_ = true;
 };
+}  // namespace test
 }  // namespace reporting
 
 #endif  // COMPONENTS_REPORTING_METRICS_FAKE_REPORTING_SETTINGS_H_
diff --git a/components/reporting/metrics/metric_data_collector.cc b/components/reporting/metrics/metric_data_collector.cc
index d1cc4bf..038d075 100644
--- a/components/reporting/metrics/metric_data_collector.cc
+++ b/components/reporting/metrics/metric_data_collector.cc
@@ -53,11 +53,12 @@
                                    MetricReportQueue* metric_report_queue,
                                    ReportingSettings* reporting_settings,
                                    const std::string& setting_path,
+                                   bool setting_enabled_default_value,
                                    base::OnceClosure on_data_reported)
     : CollectorBase(sampler, metric_report_queue),
       on_data_reported_(std::move(on_data_reported)) {
   reporting_controller_ = std::make_unique<MetricReportingController>(
-      reporting_settings, setting_path,
+      reporting_settings, setting_path, setting_enabled_default_value,
       base::BindRepeating(&OneShotCollector::Collect, base::Unretained(this)));
 }
 
@@ -86,6 +87,7 @@
                                      MetricReportQueue* metric_report_queue,
                                      ReportingSettings* reporting_settings,
                                      const std::string& enable_setting_path,
+                                     bool setting_enabled_default_value,
                                      const std::string& rate_setting_path,
                                      base::TimeDelta default_rate,
                                      int rate_unit_to_ms)
@@ -100,6 +102,7 @@
       reporting_controller_(std::make_unique<MetricReportingController>(
           reporting_settings,
           enable_setting_path,
+          setting_enabled_default_value,
           base::BindRepeating(&PeriodicEventCollector::StartPeriodicCollection,
                               base::Unretained(this)),
           base::BindRepeating(&PeriodicEventCollector::StopPeriodicCollection,
@@ -171,6 +174,7 @@
     MetricReportQueue* metric_report_queue,
     ReportingSettings* reporting_settings,
     const std::string& enable_setting_path,
+    bool setting_enabled_default_value,
     const std::string& rate_setting_path,
     base::TimeDelta default_rate,
     int rate_unit_to_ms)
@@ -178,6 +182,7 @@
                         metric_report_queue,
                         reporting_settings,
                         enable_setting_path,
+                        setting_enabled_default_value,
                         rate_setting_path,
                         default_rate,
                         rate_unit_to_ms),
diff --git a/components/reporting/metrics/metric_data_collector.h b/components/reporting/metrics/metric_data_collector.h
index 9c5aa87..7b7253fb 100644
--- a/components/reporting/metrics/metric_data_collector.h
+++ b/components/reporting/metrics/metric_data_collector.h
@@ -63,6 +63,7 @@
                    MetricReportQueue* metric_report_queue,
                    ReportingSettings* reporting_settings,
                    const std::string& setting_path,
+                   bool setting_enabled_default_value,
                    base::OnceClosure on_data_reported = base::DoNothing());
 
   OneShotCollector(const OneShotCollector& other) = delete;
@@ -91,6 +92,7 @@
                     MetricReportQueue* metric_report_queue,
                     ReportingSettings* reporting_settings,
                     const std::string& enable_setting_path,
+                    bool setting_enabled_default_value,
                     const std::string& rate_setting_path,
                     base::TimeDelta default_rate,
                     int rate_unit_to_ms = 1);
@@ -163,6 +165,7 @@
                          MetricReportQueue* metric_report_queue,
                          ReportingSettings* reporting_settings,
                          const std::string& enable_setting_path,
+                         bool setting_enabled_default_value,
                          const std::string& rate_setting_path,
                          base::TimeDelta default_rate,
                          int rate_unit_to_ms = 1);
diff --git a/components/reporting/metrics/metric_data_collector_unittest.cc b/components/reporting/metrics/metric_data_collector_unittest.cc
index d8857b1..21431f2 100644
--- a/components/reporting/metrics/metric_data_collector_unittest.cc
+++ b/components/reporting/metrics/metric_data_collector_unittest.cc
@@ -56,22 +56,30 @@
 };
 }  // namespace test
 
+namespace {
+
 class MetricDataCollectorTest : public ::testing::Test {
- public:
+ protected:
   void SetUp() override {
-    settings_ = std::make_unique<FakeReportingSettings>();
+    settings_ = std::make_unique<test::FakeReportingSettings>();
     sampler_ = std::make_unique<test::FakeSampler>();
     metric_report_queue_ = std::make_unique<test::FakeMetricReportQueue>();
   }
 
- protected:
+  void FlushTasks() {
+    base::RunLoop run_loop;
+    task_environment_.GetMainThreadTaskRunner()->PostTask(
+        FROM_HERE, run_loop.QuitClosure());
+    run_loop.Run();
+  }
+
   base::test::SingleThreadTaskEnvironment task_environment_{
       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
 
   const std::string kEnableSettingPath = "enable_path";
   const std::string kRateSettingPath = "rate_path";
 
-  std::unique_ptr<FakeReportingSettings> settings_;
+  std::unique_ptr<test::FakeReportingSettings> settings_;
   std::unique_ptr<test::FakeSampler> sampler_;
   std::unique_ptr<test::FakeMetricReportQueue> metric_report_queue_;
 };
@@ -86,6 +94,7 @@
 
   OneShotCollector collector(sampler_.get(), metric_report_queue_.get(),
                              settings_.get(), kEnableSettingPath,
+                             /*setting_enabled_default_value=*/false,
                              base::BindLambdaForTesting([&callback_called]() {
                                callback_called = true;
                              }));
@@ -100,10 +109,7 @@
   // re-enabled.
   EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
 
-  base::RunLoop run_loop;
-  task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
-                                                        run_loop.QuitClosure());
-  run_loop.Run();
+  FlushTasks();
   auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
 
   ASSERT_EQ(metric_data_reported.size(), 1ul);
@@ -120,7 +126,8 @@
   sampler_->SetMetricData(std::move(metric_data));
 
   OneShotCollector collector(sampler_.get(), metric_report_queue_.get(),
-                             settings_.get(), kEnableSettingPath);
+                             settings_.get(), kEnableSettingPath,
+                             /*setting_enabled_default_value=*/false);
 
   // Setting is initially disabled, no data is collected.
   EXPECT_EQ(sampler_->GetNumCollectCalls(), 0);
@@ -137,10 +144,7 @@
   // re-enabled.
   EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
 
-  base::RunLoop run_loop;
-  task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
-                                                        run_loop.QuitClosure());
-  run_loop.Run();
+  FlushTasks();
   auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
 
   ASSERT_EQ(metric_data_reported.size(), 1ul);
@@ -148,6 +152,46 @@
   EXPECT_TRUE(metric_data_reported[0].has_info_data());
 }
 
+TEST_F(MetricDataCollectorTest, OneShotCollector_DefaultEnabled) {
+  MetricData metric_data;
+  metric_data.mutable_info_data();
+  sampler_->SetMetricData(std::move(metric_data));
+  bool callback_called = false;
+
+  OneShotCollector collector(sampler_.get(), metric_report_queue_.get(),
+                             settings_.get(), "invalid/path",
+                             /*setting_enabled_default_value=*/true,
+                             base::BindLambdaForTesting([&callback_called]() {
+                               callback_called = true;
+                             }));
+
+  // Setting is enabled by default, data is being collected.
+  EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
+
+  FlushTasks();
+  auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+
+  ASSERT_EQ(metric_data_reported.size(), 1ul);
+  EXPECT_TRUE(callback_called);
+  EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
+  EXPECT_TRUE(metric_data_reported[0].has_info_data());
+}
+
+TEST_F(MetricDataCollectorTest, OneShotCollector_DefaultDisabled) {
+  MetricData metric_data;
+  metric_data.mutable_info_data();
+  sampler_->SetMetricData(std::move(metric_data));
+
+  OneShotCollector collector(sampler_.get(), metric_report_queue_.get(),
+                             settings_.get(), kEnableSettingPath,
+                             /*setting_enabled_default_value=*/false);
+  FlushTasks();
+
+  // Setting is disabled by default, no data is collected.
+  EXPECT_EQ(sampler_->GetNumCollectCalls(), 0);
+  EXPECT_TRUE(metric_report_queue_->GetMetricDataReported().empty());
+}
+
 TEST_F(MetricDataCollectorTest, PeriodicCollector_InitiallyEnabled) {
   constexpr int interval = 10000;
   settings_->SetBoolean(kEnableSettingPath, true);
@@ -160,7 +204,8 @@
 
   PeriodicCollector collector(
       sampler_.get(), metric_report_queue_.get(), settings_.get(),
-      kEnableSettingPath, kRateSettingPath, base::Milliseconds(interval / 2));
+      kEnableSettingPath, /*setting_enabled_default_value=*/false,
+      kRateSettingPath, base::Milliseconds(interval / 2));
 
   EXPECT_EQ(sampler_->GetNumCollectCalls(), 0);
 
@@ -191,10 +236,7 @@
   task_environment_.FastForwardBy(base::Milliseconds(interval / 2));
   EXPECT_EQ(sampler_->GetNumCollectCalls(), expected_collect_calls);
 
-  base::RunLoop run_loop;
-  task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
-                                                        run_loop.QuitClosure());
-  run_loop.Run();
+  FlushTasks();
   auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
 
   ASSERT_EQ(metric_data_reported.size(), 3ul);
@@ -219,7 +261,8 @@
 
   PeriodicCollector collector(
       sampler_.get(), metric_report_queue_.get(), settings_.get(),
-      kEnableSettingPath, kRateSettingPath, base::Milliseconds(interval / 2));
+      kEnableSettingPath, /*setting_enabled_default_value=*/false,
+      kRateSettingPath, base::Milliseconds(interval / 2));
 
   sampler_->SetMetricData(metric_data);
   task_environment_.FastForwardBy(base::Milliseconds(interval));
@@ -230,10 +273,7 @@
   task_environment_.FastForwardBy(base::Milliseconds(interval));
   EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
 
-  base::RunLoop run_loop;
-  task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
-                                                        run_loop.QuitClosure());
-  run_loop.Run();
+  FlushTasks();
   auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
 
   ASSERT_EQ(metric_data_reported.size(), 1ul);
@@ -241,6 +281,54 @@
   EXPECT_TRUE(metric_data_reported[0].has_telemetry_data());
 }
 
+TEST_F(MetricDataCollectorTest, PeriodicCollector_DefaultEnabled) {
+  constexpr int interval = 10000;
+  settings_->SetInteger(kRateSettingPath, interval);
+
+  MetricData metric_data;
+  metric_data.mutable_telemetry_data();
+
+  PeriodicCollector collector(
+      sampler_.get(), metric_report_queue_.get(), settings_.get(),
+      "invalid/path", /*setting_enabled_default_value=*/true, kRateSettingPath,
+      base::Milliseconds(interval / 2));
+
+  sampler_->SetMetricData(metric_data);
+  EXPECT_EQ(sampler_->GetNumCollectCalls(), 0);
+
+  // 10 secs elapsed, data should be collected.
+  task_environment_.FastForwardBy(base::Milliseconds(interval));
+  EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
+
+  FlushTasks();
+  auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+
+  ASSERT_EQ(metric_data_reported.size(), 1ul);
+  EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
+  EXPECT_TRUE(metric_data_reported[0].has_telemetry_data());
+}
+
+TEST_F(MetricDataCollectorTest, PeriodicCollector_DefaultDisabled) {
+  constexpr int interval = 10000;
+  settings_->SetInteger(kRateSettingPath, interval);
+
+  MetricData metric_data;
+  metric_data.mutable_telemetry_data();
+
+  PeriodicCollector collector(
+      sampler_.get(), metric_report_queue_.get(), settings_.get(),
+      "invalid/path", /*setting_enabled_default_value=*/false, kRateSettingPath,
+      base::Milliseconds(interval / 2));
+
+  sampler_->SetMetricData(metric_data);
+  task_environment_.FastForwardBy(base::Milliseconds(interval));
+  FlushTasks();
+
+  // Setting is disabled by default, no data collected.
+  EXPECT_EQ(sampler_->GetNumCollectCalls(), 0);
+  EXPECT_TRUE(metric_report_queue_->GetMetricDataReported().empty());
+}
+
 TEST_F(MetricDataCollectorTest, PeriodicEventCollector_NoAdditionalSamplers) {
   constexpr int interval = 10000;
   settings_->SetBoolean(kEnableSettingPath, true);
@@ -256,6 +344,7 @@
   PeriodicEventCollector collector(sampler_.get(), std::move(event_detector),
                                    {}, metric_report_queue_.get(),
                                    settings_.get(), kEnableSettingPath,
+                                   /*setting_enabled_default_value=*/false,
                                    kRateSettingPath, base::Milliseconds(15000));
 
   sampler_->SetMetricData(metric_data[0]);
@@ -269,10 +358,7 @@
   // Data collected and reported.
   EXPECT_EQ(sampler_->GetNumCollectCalls(), 2);
 
-  base::RunLoop run_loop;
-  task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
-                                                        run_loop.QuitClosure());
-  run_loop.Run();
+  FlushTasks();
   auto previous_metric_list = event_detector_ptr->GetPreviousMetricList();
 
   ASSERT_EQ(previous_metric_list.size(), 2ul);
@@ -334,8 +420,9 @@
   PeriodicEventCollector collector(sampler_.get(), std::move(event_detector),
                                    std::move(additional_sampler_ptrs),
                                    metric_report_queue_.get(), settings_.get(),
-                                   kEnableSettingPath, kRateSettingPath,
-                                   base::Milliseconds(15000));
+                                   kEnableSettingPath,
+                                   /*setting_enabled_default_value=*/false,
+                                   kRateSettingPath, base::Milliseconds(15000));
 
   sampler_->SetMetricData(metric_data);
   event_detector_ptr->SetHasEvent(true);
@@ -343,10 +430,7 @@
   // Data collected and reported.
   EXPECT_EQ(sampler_->GetNumCollectCalls(), 1);
 
-  base::RunLoop run_loop;
-  task_environment_.GetMainThreadTaskRunner()->PostTask(FROM_HERE,
-                                                        run_loop.QuitClosure());
-  run_loop.Run();
+  FlushTasks();
   auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
 
   ASSERT_EQ(metric_data_reported.size(), 1ul);
@@ -380,4 +464,5 @@
                                                  .https_latency_data()
                                                  .latency_ms());
 }
+}  // namespace
 }  // namespace reporting
diff --git a/components/reporting/metrics/metric_event_observer_manager.cc b/components/reporting/metrics/metric_event_observer_manager.cc
index 3e03c7b..301b967 100644
--- a/components/reporting/metrics/metric_event_observer_manager.cc
+++ b/components/reporting/metrics/metric_event_observer_manager.cc
@@ -23,6 +23,7 @@
     MetricReportQueue* metric_report_queue,
     ReportingSettings* reporting_settings,
     const std::string& enable_setting_path,
+    bool setting_enabled_default_value,
     std::vector<Sampler*> additional_samplers)
     : event_observer_(std::move(event_observer)),
       metric_report_queue_(metric_report_queue),
@@ -39,7 +40,7 @@
       base::SequencedTaskRunnerHandle::Get(), std::move(on_event_observed_cb)));
 
   reporting_controller_ = std::make_unique<MetricReportingController>(
-      reporting_settings, enable_setting_path,
+      reporting_settings, enable_setting_path, setting_enabled_default_value,
       base::BindRepeating(&MetricEventObserverManager::SetReportingEnabled,
                           base::Unretained(this),
                           /*is_enabled=*/true),
diff --git a/components/reporting/metrics/metric_event_observer_manager.h b/components/reporting/metrics/metric_event_observer_manager.h
index ecd40414..6bc2810d 100644
--- a/components/reporting/metrics/metric_event_observer_manager.h
+++ b/components/reporting/metrics/metric_event_observer_manager.h
@@ -32,6 +32,7 @@
       MetricReportQueue* metric_report_queue,
       ReportingSettings* reporting_settings,
       const std::string& enable_setting_path,
+      bool setting_enabled_default_value,
       std::vector<Sampler*> additional_samplers = {});
 
   MetricEventObserverManager(const MetricEventObserverManager& other) = delete;
diff --git a/components/reporting/metrics/metric_event_observer_manager_unittest.cc b/components/reporting/metrics/metric_event_observer_manager_unittest.cc
index e2ba97b..c1e6eb55 100644
--- a/components/reporting/metrics/metric_event_observer_manager_unittest.cc
+++ b/components/reporting/metrics/metric_event_observer_manager_unittest.cc
@@ -20,11 +20,12 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace reporting {
+namespace {
 
 class MetricEventObserverManagerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    settings_ = std::make_unique<FakeReportingSettings>();
+    settings_ = std::make_unique<test::FakeReportingSettings>();
     event_observer_ = std::make_unique<test::FakeMetricEventObserver>();
     metric_report_queue_ = std::make_unique<test::FakeMetricReportQueue>();
   }
@@ -34,7 +35,7 @@
 
   const std::string kEnableSettingPath = "enable_path";
 
-  std::unique_ptr<FakeReportingSettings> settings_;
+  std::unique_ptr<test::FakeReportingSettings> settings_;
   std::unique_ptr<test::FakeMetricEventObserver> event_observer_;
   std::unique_ptr<test::FakeMetricReportQueue> metric_report_queue_;
 };
@@ -43,9 +44,9 @@
   settings_->SetBoolean(kEnableSettingPath, true);
   auto* event_observer_ptr = event_observer_.get();
 
-  MetricEventObserverManager event_manager(std::move(event_observer_),
-                                           metric_report_queue_.get(),
-                                           settings_.get(), kEnableSettingPath);
+  MetricEventObserverManager event_manager(
+      std::move(event_observer_), metric_report_queue_.get(), settings_.get(),
+      kEnableSettingPath, /*setting_enabled_default_value=*/false);
 
   MetricData metric_data;
   metric_data.mutable_event_data();
@@ -76,9 +77,9 @@
   settings_->SetBoolean(kEnableSettingPath, false);
   auto* event_observer_ptr = event_observer_.get();
 
-  MetricEventObserverManager event_manager(std::move(event_observer_),
-                                           metric_report_queue_.get(),
-                                           settings_.get(), kEnableSettingPath);
+  MetricEventObserverManager event_manager(
+      std::move(event_observer_), metric_report_queue_.get(), settings_.get(),
+      kEnableSettingPath, /*setting_enabled_default_value=*/false);
 
   MetricData metric_data;
   metric_data.mutable_event_data();
@@ -98,6 +99,41 @@
   EXPECT_TRUE(metric_data_reported[0].has_event_data());
 }
 
+TEST_F(MetricEventObserverManagerTest, DefaultEnabled) {
+  auto* event_observer_ptr = event_observer_.get();
+
+  MetricEventObserverManager event_manager(
+      std::move(event_observer_), metric_report_queue_.get(), settings_.get(),
+      "invalid/path", /*setting_enabled_default_value=*/true);
+
+  MetricData metric_data;
+  metric_data.mutable_event_data();
+
+  ASSERT_TRUE(event_observer_ptr->GetReportingEnabled());
+  event_observer_ptr->RunCallback(metric_data);
+
+  auto metric_data_reported = metric_report_queue_->GetMetricDataReported();
+  ASSERT_EQ(metric_data_reported.size(), 1ul);
+  EXPECT_TRUE(metric_data_reported[0].has_timestamp_ms());
+  EXPECT_TRUE(metric_data_reported[0].has_event_data());
+}
+
+TEST_F(MetricEventObserverManagerTest, DefaultDisabled) {
+  auto* event_observer_ptr = event_observer_.get();
+
+  MetricEventObserverManager event_manager(
+      std::move(event_observer_), metric_report_queue_.get(), settings_.get(),
+      "invalid/path", /*setting_enabled_default_value=*/false);
+
+  MetricData metric_data;
+  metric_data.mutable_event_data();
+
+  event_observer_ptr->RunCallback(metric_data);
+
+  ASSERT_FALSE(event_observer_ptr->GetReportingEnabled());
+  EXPECT_TRUE(metric_report_queue_->GetMetricDataReported().empty());
+}
+
 TEST_F(MetricEventObserverManagerTest, AdditionalSamplers) {
   settings_->SetBoolean(kEnableSettingPath, true);
   auto* event_observer_ptr = event_observer_.get();
@@ -109,7 +145,8 @@
 
   MetricEventObserverManager event_manager(
       std::move(event_observer_), metric_report_queue_.get(), settings_.get(),
-      kEnableSettingPath, {&additional_sampler});
+      kEnableSettingPath, /*setting_enabled_default_value=*/false,
+      {&additional_sampler});
 
   MetricData metric_data;
   metric_data.mutable_event_data();
@@ -130,4 +167,6 @@
     EXPECT_TRUE(metric_data_reported[i].has_telemetry_data());
   }
 }
+
+}  // namespace
 }  // namespace reporting
diff --git a/components/reporting/metrics/metric_rate_controller_unittest.cc b/components/reporting/metrics/metric_rate_controller_unittest.cc
index 0d0bc9ac..edbe911 100644
--- a/components/reporting/metrics/metric_rate_controller_unittest.cc
+++ b/components/reporting/metrics/metric_rate_controller_unittest.cc
@@ -14,11 +14,12 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace reporting {
+namespace {
 
 class MetricRateControllerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    settings_ = std::make_unique<FakeReportingSettings>();
+    settings_ = std::make_unique<test::FakeReportingSettings>();
     run_count_ = 0;
     run_cb_ = base::BindLambdaForTesting([this]() { ++run_count_; });
   }
@@ -26,7 +27,7 @@
  protected:
   const std::string rate_setting_path_ = "rate_path";
 
-  std::unique_ptr<FakeReportingSettings> settings_;
+  std::unique_ptr<test::FakeReportingSettings> settings_;
 
   int run_count_ = 0;
   base::RepeatingClosure run_cb_;
@@ -144,4 +145,5 @@
   task_environment_.FastForwardBy(base::Milliseconds(5000));
   EXPECT_EQ(run_count_, 1);
 }
+}  // namespace
 }  // namespace reporting
diff --git a/components/reporting/metrics/metric_report_queue_unittest.cc b/components/reporting/metrics/metric_report_queue_unittest.cc
index 94ed245..7f675a0 100644
--- a/components/reporting/metrics/metric_report_queue_unittest.cc
+++ b/components/reporting/metrics/metric_report_queue_unittest.cc
@@ -21,21 +21,22 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace reporting {
-
 using ::testing::_;
 
+namespace reporting {
+namespace {
+
 class MetricReportQueueTest : public ::testing::Test {
  public:
   void SetUp() override {
     priority_ = Priority::SLOW_BATCH;
-    settings_ = std::make_unique<FakeReportingSettings>();
+    settings_ = std::make_unique<test::FakeReportingSettings>();
   }
 
  protected:
   const std::string kRateSettingPath = "rate_path";
 
-  std::unique_ptr<FakeReportingSettings> settings_;
+  std::unique_ptr<test::FakeReportingSettings> settings_;
 
   Priority priority_;
 
@@ -152,4 +153,5 @@
   EXPECT_CALL(*mock_queue_ptr, Flush(priority_, _)).Times(1);
   task_environment_.FastForwardBy(base::Milliseconds(rate_ms));
 }
+}  // namespace
 }  // namespace reporting
diff --git a/components/reporting/metrics/metric_reporting_controller.cc b/components/reporting/metrics/metric_reporting_controller.cc
index f77283b..3c87000 100644
--- a/components/reporting/metrics/metric_reporting_controller.cc
+++ b/components/reporting/metrics/metric_reporting_controller.cc
@@ -14,10 +14,12 @@
 MetricReportingController::MetricReportingController(
     ReportingSettings* reporting_settings,
     const std::string& setting_path,
+    bool setting_enabled_default_value,
     base::RepeatingClosure on_setting_enabled,
     base::RepeatingClosure on_setting_disabled)
     : reporting_settings_(reporting_settings),
       setting_path_(setting_path),
+      setting_enabled_default_value_(setting_enabled_default_value),
       on_setting_enabled_(std::move(on_setting_enabled)),
       on_setting_disabled_(std::move(on_setting_disabled)) {
   UpdateSetting();
@@ -44,7 +46,7 @@
     return;
   }
 
-  bool new_setting_enabled = false;
+  bool new_setting_enabled = setting_enabled_default_value_;
   reporting_settings_->GetBoolean(setting_path_, &new_setting_enabled);
 
   if (setting_enabled_ != new_setting_enabled) {
diff --git a/components/reporting/metrics/metric_reporting_controller.h b/components/reporting/metrics/metric_reporting_controller.h
index adebe4b5..2438659 100644
--- a/components/reporting/metrics/metric_reporting_controller.h
+++ b/components/reporting/metrics/metric_reporting_controller.h
@@ -25,6 +25,7 @@
   MetricReportingController(
       ReportingSettings* reporting_settings,
       const std::string& setting_path,
+      bool setting_enabled_default_value,
       base::RepeatingClosure on_setting_enabled,
       base::RepeatingClosure on_setting_disabled = base::DoNothing());
 
@@ -39,6 +40,7 @@
 
   const raw_ptr<ReportingSettings> reporting_settings_;
   const std::string setting_path_;
+  const bool setting_enabled_default_value_;
   const base::RepeatingClosure on_setting_enabled_;
   const base::RepeatingClosure on_setting_disabled_;
 
diff --git a/components/reporting/metrics/metric_reporting_controller_unittest.cc b/components/reporting/metrics/metric_reporting_controller_unittest.cc
index 7964a1a..bcad021 100644
--- a/components/reporting/metrics/metric_reporting_controller_unittest.cc
+++ b/components/reporting/metrics/metric_reporting_controller_unittest.cc
@@ -14,11 +14,14 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace reporting {
+namespace {
+
+constexpr char kSettingPath[] = "path";
 
 class MetricReportingControllerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    settings_ = std::make_unique<FakeReportingSettings>();
+    settings_ = std::make_unique<test::FakeReportingSettings>();
     enable_count_ = 0;
     disable_count_ = 0;
     enable_cb_ = base::BindLambdaForTesting([this]() { ++enable_count_; });
@@ -31,26 +34,36 @@
   base::RepeatingClosure enable_cb_;
   base::RepeatingClosure disable_cb_;
 
-  std::unique_ptr<FakeReportingSettings> settings_;
+  std::unique_ptr<test::FakeReportingSettings> settings_;
 
   base::test::SingleThreadTaskEnvironment task_environment_;
 };
 
-TEST_F(MetricReportingControllerTest, InvalidPath) {
-  MetricReportingController controller(settings_.get(), "invalid_path",
+TEST_F(MetricReportingControllerTest, InvalidPath_DefaultDisabled) {
+  MetricReportingController controller(settings_.get(), "invalid/path",
+                                       /*setting_enabled_default_value=*/false,
                                        enable_cb_, disable_cb_);
 
   EXPECT_EQ(enable_count_, 0);
   EXPECT_EQ(disable_count_, 0);
 }
 
+TEST_F(MetricReportingControllerTest, InvalidPath_DefaultEnabled) {
+  MetricReportingController controller(settings_.get(), "invalid/path",
+                                       /*setting_enabled_default_value=*/true,
+                                       enable_cb_, disable_cb_);
+
+  EXPECT_EQ(enable_count_, 1);
+  EXPECT_EQ(disable_count_, 0);
+}
+
 TEST_F(MetricReportingControllerTest, TrustedCheck) {
-  const std::string path = "path";
-  settings_->SetBoolean(path, true);
+  settings_->SetBoolean(kSettingPath, true);
   settings_->SetIsTrusted(false);
 
-  MetricReportingController controller(settings_.get(), path, enable_cb_,
-                                       disable_cb_);
+  MetricReportingController controller(settings_.get(), kSettingPath,
+                                       /*setting_enabled_default_value=*/false,
+                                       enable_cb_, disable_cb_);
 
   EXPECT_EQ(enable_count_, 0);
   EXPECT_EQ(disable_count_, 0);
@@ -61,55 +74,57 @@
   EXPECT_EQ(disable_count_, 0);
 }
 
-TEST_F(MetricReportingControllerTest, DefaultEnable) {
-  const std::string path = "path";
-  settings_->SetBoolean(path, true);
+TEST_F(MetricReportingControllerTest, InitiallyEnabled) {
+  settings_->SetBoolean(kSettingPath, true);
 
-  MetricReportingController controller(settings_.get(), path, enable_cb_,
-                                       disable_cb_);
+  MetricReportingController controller(settings_.get(), kSettingPath,
+                                       /*setting_enabled_default_value=*/false,
+                                       enable_cb_, disable_cb_);
 
   // Only enable_cb_ is called.
   EXPECT_EQ(enable_count_, 1);
   EXPECT_EQ(disable_count_, 0);
 
   // Change to disable.
-  settings_->SetBoolean(path, false);
+  settings_->SetBoolean(kSettingPath, false);
 
   // Only disable_cb_ is called.
   EXPECT_EQ(enable_count_, 1);
   EXPECT_EQ(disable_count_, 1);
 
   // Change to enable.
-  settings_->SetBoolean(path, true);
+  settings_->SetBoolean(kSettingPath, true);
 
   // Only enable_cb_ is called.
   EXPECT_EQ(enable_count_, 2);
   EXPECT_EQ(disable_count_, 1);
 }
 
-TEST_F(MetricReportingControllerTest, DefaultDisable) {
-  const std::string path = "path";
-  settings_->SetBoolean(path, false);
+TEST_F(MetricReportingControllerTest, InitiallyDisabled) {
+  settings_->SetBoolean(kSettingPath, false);
 
-  MetricReportingController controller(settings_.get(), path, enable_cb_,
-                                       disable_cb_);
+  MetricReportingController controller(settings_.get(), kSettingPath,
+                                       /*setting_enabled_default_value=*/false,
+                                       enable_cb_, disable_cb_);
 
   // No callbacks are called.
   EXPECT_EQ(enable_count_, 0);
   EXPECT_EQ(disable_count_, 0);
 
   // Change to enable.
-  settings_->SetBoolean(path, true);
+  settings_->SetBoolean(kSettingPath, true);
 
   // Only enable_cb_ is called.
   EXPECT_EQ(enable_count_, 1);
   EXPECT_EQ(disable_count_, 0);
 
   // Change to disable.
-  settings_->SetBoolean(path, false);
+  settings_->SetBoolean(kSettingPath, false);
 
   // Only disable_cb_ is called.
   EXPECT_EQ(enable_count_, 1);
   EXPECT_EQ(disable_count_, 1);
 }
+
+}  // namespace
 }  // namespace reporting
diff --git a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
index 7cbce72..f43f095 100644
--- a/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
+++ b/components/segmentation_platform/internal/dummy_segmentation_platform_service.cc
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "base/threading/sequenced_task_runner_handle.h"
+#include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/segment_selection_result.h"
 
 namespace segmentation_platform {
@@ -18,6 +19,8 @@
 void DummySegmentationPlatformService::GetSelectedSegment(
     const std::string& segmentation_key,
     SegmentSelectionCallback callback) {
+  stats::RecordSegmentSelectionFailure(
+      stats::SegmentationSelectionFailureReason::kPlatformDisabled);
   base::SequencedTaskRunnerHandle::Get()->PostTask(
       FROM_HERE, base::BindOnce(std::move(callback), SegmentSelectionResult()));
 }
diff --git a/components/segmentation_platform/internal/execution/segmentation_model_handler.cc b/components/segmentation_platform/internal/execution/segmentation_model_handler.cc
index 70a26fd..42d59a49 100644
--- a/components/segmentation_platform/internal/execution/segmentation_model_handler.cc
+++ b/components/segmentation_platform/internal/execution/segmentation_model_handler.cc
@@ -27,7 +27,11 @@
           std::make_unique<SegmentationModelExecutor>(),
           optimization_target,
           /*model_metadata=*/absl::nullopt),
-      model_updated_callback_(model_updated_callback) {}
+      model_updated_callback_(model_updated_callback) {
+  stats::RecordModelAvailability(
+      optimization_target,
+      stats::SegmentationModelAvailability::kModelHandlerCreated);
+}
 
 SegmentationModelHandler::~SegmentationModelHandler() = default;
 
@@ -53,8 +57,14 @@
     // expected to pass this along. Either something failed horribly on the way,
     // we failed to read the metadata, or the server side configuration is
     // wrong.
+    stats::RecordModelAvailability(
+        optimization_target,
+        stats::SegmentationModelAvailability::kMetadataInvalid);
     return;
   }
+  stats::RecordModelAvailability(
+      optimization_target,
+      stats::SegmentationModelAvailability::kModelAvailable);
 
   model_updated_callback_.Run(optimization_target,
                               std::move(*segmentation_model_metadata));
diff --git a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
index fc7c97d..a39ff7167 100644
--- a/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
+++ b/components/segmentation_platform/internal/scheduler/model_execution_scheduler_impl.cc
@@ -86,6 +86,10 @@
     segment_result.set_timestamp_us(
         clock_->Now().ToDeltaSinceWindowsEpoch().InMicroseconds());
     stats::RecordModelScore(segment_id, result.first);
+  } else {
+    stats::RecordSegmentSelectionFailure(
+        stats::SegmentationSelectionFailureReason::
+            kAtLeastOneModelFailedExecution);
   }
 
   segment_database_->SaveSegmentResult(
@@ -137,6 +141,9 @@
   // Filter out segments that don't match signal collection min length.
   if (!signal_storage_config_->MeetsSignalCollectionRequirement(
           segment_info.model_metadata())) {
+    stats::RecordSegmentSelectionFailure(
+        stats::SegmentationSelectionFailureReason::
+            kAtLeastOneModelNeedsMoreSignals);
     VLOG(1) << "Segmentation model not executed since metadata requirements "
                "not met.";
     return false;
@@ -157,8 +164,11 @@
 void ModelExecutionSchedulerImpl::OnResultSaved(OptimizationTarget segment_id,
                                                 bool success) {
   stats::RecordModelExecutionSaveResult(segment_id, success);
-  if (!success)
+  if (!success) {
+    stats::RecordSegmentSelectionFailure(
+        stats::SegmentationSelectionFailureReason::kFailedToSaveModelResult);
     return;
+  }
 
   for (Observer* observer : observers_)
     observer->OnModelExecutionCompleted(segment_id);
diff --git a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
index 1150f3e..e195313 100644
--- a/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
+++ b/components/segmentation_platform/internal/segmentation_platform_service_impl.cc
@@ -37,6 +37,7 @@
 #include "components/segmentation_platform/internal/signals/histogram_signal_handler.h"
 #include "components/segmentation_platform/internal/signals/signal_filter_processor.h"
 #include "components/segmentation_platform/internal/signals/user_action_signal_handler.h"
+#include "components/segmentation_platform/internal/stats.h"
 #include "components/segmentation_platform/public/config.h"
 
 using optimization_guide::proto::OptimizationTarget;
@@ -207,8 +208,11 @@
                       signal_storage_config_initialized_;
 
   OnServiceStatusChanged();
-  if (!init_success)
+  if (!init_success) {
+    stats::RecordSegmentSelectionFailure(
+        stats::SegmentationSelectionFailureReason::kDBInitFailure);
     return;
+  }
 
   model_execution_manager_ = CreateModelExecutionManager(
       model_provider_, task_runner_, all_segment_ids_, clock_,
diff --git a/components/segmentation_platform/internal/selection/segment_selector_impl.cc b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
index 73a6f60..020c027 100644
--- a/components/segmentation_platform/internal/selection/segment_selector_impl.cc
+++ b/components/segmentation_platform/internal/selection/segment_selector_impl.cc
@@ -42,6 +42,12 @@
   if (selected_segment.has_value()) {
     selected_segment_last_session_.segment = selected_segment->segment_id;
     selected_segment_last_session_.is_ready = true;
+    stats::RecordSegmentSelectionFailure(
+        stats::SegmentationSelectionFailureReason::kSelectionAvailableInPrefs);
+  } else {
+    stats::RecordSegmentSelectionFailure(
+        stats::SegmentationSelectionFailureReason::
+            kInvalidSelectionResultInPrefs);
   }
 }
 
@@ -94,6 +100,9 @@
       VLOG(1) << __func__ << ": segment="
               << OptimizationTarget_Name(segment_info.segment_id())
               << " does not meet signal collection requirements.";
+      stats::RecordSegmentSelectionFailure(
+          stats::SegmentationSelectionFailureReason::
+              kAtLeastOneSegmentSignalsNotCollected);
       return false;
     }
 
@@ -102,6 +111,9 @@
       VLOG(1) << __func__ << ": segment="
               << OptimizationTarget_Name(segment_info.segment_id())
               << " has expired or unavailable result.";
+      stats::RecordSegmentSelectionFailure(
+          stats::SegmentationSelectionFailureReason::
+              kAtLeastOneSegmentNotReady);
       return false;
     }
   }
@@ -117,6 +129,8 @@
                                      : config_->segment_selection_ttl;
     if (!platform_options_.force_refresh_results &&
         previous_selection->selection_time + ttl_to_use > clock_->Now()) {
+      stats::RecordSegmentSelectionFailure(
+          stats::SegmentationSelectionFailureReason::kSelectionTtlNotExpired);
       VLOG(1) << __func__ << ": previous selection of segment="
               << OptimizationTarget_Name(previous_selection->segment_id)
               << " has not yet expired.";
diff --git a/components/segmentation_platform/internal/stats.cc b/components/segmentation_platform/internal/stats.cc
index cf3150e..16fc93d3 100644
--- a/components/segmentation_platform/internal/stats.cc
+++ b/components/segmentation_platform/internal/stats.cc
@@ -514,5 +514,18 @@
       histogram_value_count);
 }
 
+void RecordSegmentSelectionFailure(SegmentationSelectionFailureReason reason) {
+  base::UmaHistogramEnumeration("SegmentationPlatform.SelectionFailedReason",
+                                reason);
+}
+
+void RecordModelAvailability(OptimizationTarget segment_id,
+                             SegmentationModelAvailability availability) {
+  base::UmaHistogramEnumeration(
+      "SegmentationPlatform.ModelAvailability." +
+          OptimizationTargetToHistogramVariant(segment_id),
+      availability);
+}
+
 }  // namespace stats
 }  // namespace segmentation_platform
diff --git a/components/segmentation_platform/internal/stats.h b/components/segmentation_platform/internal/stats.h
index ce96c5b5..91f3713d 100644
--- a/components/segmentation_platform/internal/stats.h
+++ b/components/segmentation_platform/internal/stats.h
@@ -9,6 +9,7 @@
 #include "components/segmentation_platform/internal/database/metadata_utils.h"
 #include "components/segmentation_platform/internal/execution/model_execution_status.h"
 #include "components/segmentation_platform/internal/proto/types.pb.h"
+#include "components/segmentation_platform/public/segment_selection_result.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 
 using optimization_guide::proto::OptimizationTarget;
@@ -143,6 +144,41 @@
     const std::set<uint64_t>& user_actions,
     const std::set<std::pair<std::string, proto::SignalType>>& histograms);
 
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. Please keep in sync with
+// "SegmentationSelectionFailureReason" in
+// //tools/metrics/histograms/enums.xml.
+enum class SegmentationSelectionFailureReason {
+  kPlatformDisabled = 0,
+  kSelectionAvailableInPrefs = 1,
+  kAtLeastOneSegmentNotReady = 2,
+  kAtLeastOneSegmentSignalsNotCollected = 3,
+  kSelectionTtlNotExpired = 4,
+  kAtLeastOneModelFailedExecution = 5,
+  kAtLeastOneModelNeedsMoreSignals = 6,
+  kAtLeastOneModelWithInvalidMetadata = 7,
+  kFailedToSaveModelResult = 8,
+  kInvalidSelectionResultInPrefs = 9,
+  kDBInitFailure = 10,
+  kMaxValue = kDBInitFailure
+};
+
+// Records the reason for failure or success to compute a segment selection.
+void RecordSegmentSelectionFailure(SegmentationSelectionFailureReason reason);
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused. Please keep in sync with
+// "SegmentationModelAvailability" in //tools/metrics/histograms/enums.xml.
+enum class SegmentationModelAvailability {
+  kModelHandlerCreated = 0,
+  kModelAvailable = 1,
+  kMetadataInvalid = 2,
+  kMaxValue = kMetadataInvalid
+};
+// Records the availability of segmentation models for each target needed.
+void RecordModelAvailability(OptimizationTarget segment_id,
+                             SegmentationModelAvailability availability);
+
 }  // namespace stats
 }  // namespace segmentation_platform
 
diff --git a/components/ukm/ukm_recorder_impl.cc b/components/ukm/ukm_recorder_impl.cc
index c24b930..d555224 100644
--- a/components/ukm/ukm_recorder_impl.cc
+++ b/components/ukm/ukm_recorder_impl.cc
@@ -333,6 +333,7 @@
     StoreEntryProto(*entry, proto_entry);
     source_ids_seen.insert(entry->source_id);
   }
+
   // Number of sources excluded from this report because no entries referred to
   // them.
   const int num_sources_unsent =
@@ -346,6 +347,7 @@
 
   // Number of sources discarded due to not matching a navigation URL.
   int num_sources_unmatched = 0;
+
   std::unordered_map<SourceIdType, int> serialized_source_type_counts;
 
   for (const auto& kv : recordings_.sources) {
@@ -375,11 +377,10 @@
       // Omit entryless sources from the report.
       if (!base::Contains(source_ids_seen, kv.first)) {
         continue;
-      } else {
-        // Source of ukm::SourceIdObj::Type::DEFAULT type will not be kept
-        // after entries are logged.
-        MarkSourceForDeletion(kv.first);
       }
+
+      // Non-whitelisted Source types will not be kept after entries are logged.
+      MarkSourceForDeletion(kv.first);
     }
     // Minimal validations before serializing into a proto message.
     // See crbug/1274876.
@@ -437,8 +438,8 @@
     }
   }
   int num_serialized_sources = 0;
-  for (const auto& entry : serialized_source_type_counts) {
-    num_serialized_sources += entry.second;
+  for (const auto& source_type_and_count : serialized_source_type_counts) {
+    num_serialized_sources += source_type_and_count.second;
   }
 
   UMA_HISTOGRAM_COUNTS_1000("UKM.Sources.SerializedCount2",
@@ -502,16 +503,65 @@
   report->set_is_continuous(recording_is_continuous_);
   recording_is_continuous_ = true;
 
-  // Defer at most GetMaxKeptSources() sources to the next report,
-  // prioritizing most recently created ones.
-  int pruned_sources_age = PruneOldSources(max_kept_sources_);
+  // Modify the set source_ids_seen by removing sources that aren't in
+  // recordings_. We do this here as there is a few places for
+  // recordings_.sources to be modified. The resulting set will be currently
+  // existing sources that were seen in this report.
+  auto it = source_ids_seen.begin();
+  while (it != source_ids_seen.end()) {
+    if (!base::Contains(recordings_.sources, *it)) {
+      it = source_ids_seen.erase(it);
+    } else {
+      it++;
+    }
+  }
+
+  // Build the set of sources that exist in recordings_.sources that were not
+  // seen in this report.
+  std::set<SourceId> source_ids_unseen;
+  for (const auto& kv : recordings_.sources) {
+    if (!base::Contains(source_ids_seen, kv.first)) {
+      source_ids_unseen.insert(kv.first);
+    }
+  }
+
+  int pruned_sources_age_sec = 0;
+  // Setup an experiment to test what will occur if we prune unseen sources
+  // first.
+  if (base::GetFieldTrialParamByFeatureAsBool(
+          kUkmFeature, "PruneUnseenSourcesFirst", false)) {
+    int pruned_sources_age_from_unseen_sec =
+        PruneOldSources(max_kept_sources_, source_ids_unseen);
+
+    // Prune again from seen sources. Note that if we've already pruned enough
+    // from the unseen sources, this will be a noop.
+    int pruned_sources_age_from_seen_sec =
+        PruneOldSources(max_kept_sources_, source_ids_seen);
+
+    // We're looking for the newest age, which will be the largest between the
+    // two sets we pruned from.
+    pruned_sources_age_sec = std::max(pruned_sources_age_from_unseen_sec,
+                                      pruned_sources_age_from_seen_sec);
+
+  } else {
+    // In this case, we prune all sources without caring if they were seen or
+    // not. Make a set of all existing sources so we can use the same
+    // PruneOldSources method.
+    std::set<SourceId> all_sources;
+    for (const auto& kv : recordings_.sources) {
+      all_sources.insert(kv.first);
+    }
+
+    pruned_sources_age_sec = PruneOldSources(max_kept_sources_, all_sources);
+  }
+
   // Record how old the newest truncated source is.
-  source_counts_proto->set_pruned_sources_age_seconds(pruned_sources_age);
+  source_counts_proto->set_pruned_sources_age_seconds(pruned_sources_age_sec);
 
   // Set deferred sources count after pruning.
   source_counts_proto->set_deferred_sources(recordings_.sources.size());
-  // Same value as the deferred source count, for setting the carryover count in
-  // the next reporting cycle.
+  // Same value as the deferred source count, for setting the carryover count
+  // in the next reporting cycle.
   recordings_.source_counts.carryover_sources = recordings_.sources.size();
 
   // We already matched these deferred sources against the URL whitelist.
@@ -565,29 +615,62 @@
   return true;
 }
 
-int UkmRecorderImpl::PruneOldSources(size_t max_kept_sources) {
-  if (recordings_.sources.size() <= max_kept_sources)
+int UkmRecorderImpl::PruneOldSources(size_t max_kept_sources,
+                                     const std::set<SourceId>& pruning_set) {
+  long num_prune_required = recordings_.sources.size() - max_kept_sources;
+  // In either case here, nothing to be done.
+  if (num_prune_required <= 0 || pruning_set.size() == 0)
     return 0;
 
-  std::vector<std::pair<base::TimeTicks, SourceId>> timestamp_source_id_pairs;
-  for (const auto& kv : recordings_.sources) {
-    timestamp_source_id_pairs.push_back(
-        std::make_pair(kv.second->creation_time(), kv.first));
+  // We can prune everything, so let's do that directly.
+  if (static_cast<unsigned long>(num_prune_required) >= pruning_set.size()) {
+    base::TimeTicks pruned_sources_age = base::TimeTicks();
+    for (const auto& source_id : pruning_set) {
+      auto creation_time = recordings_.sources[source_id]->creation_time();
+      if (creation_time > pruned_sources_age)
+        pruned_sources_age = creation_time;
+
+      recordings_.sources.erase(source_id);
+    }
+    base::TimeDelta age_delta = base::TimeTicks::Now() - pruned_sources_age;
+    // Technically the age we return here isn't quite right, this is the age of
+    // the newest element of the pruned set, while we actually want the age of
+    // the last one kept. However it's very unlikely to make a difference in
+    // practice as if all are pruned here, it is very likely we'll need to prune
+    // from the seen set next. Since it would be logically quite a bit more
+    // complex to get this exactly right, it's ok for this to be very slightly
+    // off in an edge case just to keep complexity down.
+    return age_delta.InSeconds();
   }
-  // Partially sort so that the last |max_kept_sources| elements are the
+
+  // In this case we cannot prune everything, so we will select only the oldest
+  // sources to prune.
+
+  // Build a list of timestamp->source pairs for all source we consider for
+  // pruning.
+  std::vector<std::pair<base::TimeTicks, SourceId>> timestamp_source_id_pairs;
+  for (const auto& source_id : pruning_set) {
+    auto creation_time = recordings_.sources[source_id]->creation_time();
+    timestamp_source_id_pairs.emplace_back(
+        std::make_pair(creation_time, source_id));
+  }
+
+  // Partially sort so that the last |num_prune_required| elements are the
   // newest.
   std::nth_element(timestamp_source_id_pairs.begin(),
-                   timestamp_source_id_pairs.end() - max_kept_sources,
+                   timestamp_source_id_pairs.end() - num_prune_required,
                    timestamp_source_id_pairs.end());
 
-  for (auto kv = timestamp_source_id_pairs.begin();
-       kv != timestamp_source_id_pairs.end() - max_kept_sources; ++kv) {
-    recordings_.sources.erase(kv->second);
+  // Actually prune |num_prune_required| sources.
+  for (int i = 0; i < num_prune_required; i++) {
+    auto source_id = timestamp_source_id_pairs[i].second;
+    recordings_.sources.erase(source_id);
   }
 
   base::TimeDelta pruned_sources_age =
       base::TimeTicks::Now() -
-      (timestamp_source_id_pairs.end() - (max_kept_sources + 1))->first;
+      (timestamp_source_id_pairs.end() - (num_prune_required + 1))->first;
+
   return pruned_sources_age.InSeconds();
 }
 
diff --git a/components/ukm/ukm_recorder_impl.h b/components/ukm/ukm_recorder_impl.h
index 1772d41..bb213d5a 100644
--- a/components/ukm/ukm_recorder_impl.h
+++ b/components/ukm/ukm_recorder_impl.h
@@ -131,9 +131,11 @@
   }
 
   // Keep only newest |max_kept_sources| sources when the number of sources
-  // in recordings_ exceeds this threshold. Returns the age of newest truncated
+  // in recordings_ exceeds this threshold. We only consider the set of ids
+  // contained in |pruning_set|. Returns the age of newest truncated
   // source in seconds.
-  int PruneOldSources(size_t max_kept_sources);
+  int PruneOldSources(size_t max_kept_sources,
+                      const std::set<SourceId>& pruning_set);
 
   // UkmRecorder:
   void AddEntry(mojom::UkmEntryPtr entry) override;
diff --git a/components/ukm/ukm_service_unittest.cc b/components/ukm/ukm_service_unittest.cc
index c3c0e55..e688896d 100644
--- a/components/ukm/ukm_service_unittest.cc
+++ b/components/ukm/ukm_service_unittest.cc
@@ -1582,4 +1582,116 @@
       proto_report.aggregates(0).metrics(0).has_dropped_due_to_filter());
 }
 
+TEST_F(UkmServiceTest, PruneUnseenFirst) {
+  // We will be testing with the prune unseen feature both off and on.
+  for (bool prune_unseen_sources_first : {true, false}) {
+    const GURL kURL("https://google.com/foobar");
+
+    // Set the 'MaxKeptSources' value to 3 so it is easier to test.
+    ScopedUkmFeatureParams params(
+        {{"MaxKeptSources", "3"},
+         {"PruneUnseenSourcesFirst",
+          prune_unseen_sources_first ? "true" : "false"}});
+
+    ClearPrefs();
+    UkmService service(&prefs_, &client_,
+                       std::make_unique<MockDemographicMetricsProvider>());
+    TestRecordingHelper recorder(&service);
+    EXPECT_EQ(0, GetPersistedLogCount());
+    service.Initialize();
+    task_runner_->RunUntilIdle();
+    service.EnableRecording(/*extensions=*/false);
+    service.EnableReporting();
+
+    // Create 5 whitelisted ids. Whitelisted ids (like APP_ID) will not be
+    // automatically removed when they emit events. They're only removed via the
+    // pruning mechanism. Note that the are added in order, so 4 is the
+    // youngest/newest.
+    std::vector<SourceId> ids;
+    base::TimeTicks last_time = base::TimeTicks::Now();
+    for (int i = 0; i < 5; ++i) {
+      // Wait until base::TimeTicks::Now() no longer equals |last_time|. This
+      // ensures each source has a unique timestamp to avoid flakes. Should take
+      // between 1-15ms per documented resolution of base::TimeTicks.
+      while (base::TimeTicks::Now() == last_time) {
+        base::PlatformThread::Sleep(base::Milliseconds(1));
+      }
+      ids.push_back(GetWhitelistedSourceId(i));
+      recorder.UpdateSourceURL(ids.back(), kURL);
+      last_time = base::TimeTicks::Now();
+    }
+
+    // Events on 0 and 4. This will be important to this test, as we are testing
+    // how pruning will vary based on this. So keep this in mind.
+    TestEvent1(ids[0]).Record(&service);
+    TestEvent1(ids[4]).Record(&service);
+
+    service.Flush();
+    EXPECT_EQ(1, GetPersistedLogCount());
+    auto proto_report = GetPersistedReport();
+
+    EXPECT_EQ(5, proto_report.source_counts().observed());
+    // All are navigation sources.
+    EXPECT_EQ(5, proto_report.source_counts().navigation_sources());
+    EXPECT_EQ(0, proto_report.source_counts().unmatched_sources());
+
+    // In all cases, 3 will be deferred since that is our max allowed.
+    EXPECT_EQ(3, proto_report.source_counts().deferred_sources());
+    // This is from last time, so none there.
+    EXPECT_EQ(0, proto_report.source_counts().carryover_sources());
+
+    // All 5 sources will be included in this first report.
+    ASSERT_EQ(5, proto_report.sources_size());
+    EXPECT_EQ(ids[0], proto_report.sources(0).id());
+    EXPECT_EQ(ids[1], proto_report.sources(1).id());
+    EXPECT_EQ(ids[2], proto_report.sources(2).id());
+    EXPECT_EQ(ids[3], proto_report.sources(3).id());
+    EXPECT_EQ(ids[4], proto_report.sources(4).id());
+
+    // Depending on the PruneUnseenSourcesFirst setting, different ones will be
+    // removed.
+    // We have MaxKeptSources=3.
+    // If PruneUnseenSourcesFirst was set, then the ones kept should be the two
+    // that were used, which are 0 and 4. The one remaining one will be picked
+    // via age which will be 3, so 0, 3, 4 are kept.
+    // Otherwise, it will be entirely based on age, which is 2,3,4.
+
+    // New events on 0,2,4. This actually doesn't matter with respect to what
+    // sources are emitted here, as some sources are already pruned.
+    TestEvent1(ids[0]).Record(&service);
+    TestEvent1(ids[2]).Record(&service);
+    TestEvent1(ids[4]).Record(&service);
+
+    service.Flush();
+    EXPECT_EQ(2, GetPersistedLogCount());
+    proto_report = GetPersistedReport();
+
+    // No new sources observed.
+    EXPECT_EQ(0, proto_report.source_counts().observed());
+    // 0 again, as this is for newly observed ones.
+    EXPECT_EQ(0, proto_report.source_counts().navigation_sources());
+    EXPECT_EQ(0, proto_report.source_counts().unmatched_sources());
+
+    // Since no new sources added, we still are keeping the same 3. So all 3 are
+    // kept and retained, in both cases.
+    EXPECT_EQ(3, proto_report.source_counts().deferred_sources());
+    EXPECT_EQ(3, proto_report.source_counts().carryover_sources());
+    ASSERT_EQ(3, proto_report.sources_size());
+
+    if (prune_unseen_sources_first) {
+      // 0, 3, 4 as 0 and 4 were used last time, and 3 is the newest of the
+      // remaining.
+      EXPECT_EQ(ids[0], proto_report.sources(0).id());
+      EXPECT_EQ(ids[3], proto_report.sources(1).id());
+      EXPECT_EQ(ids[4], proto_report.sources(2).id());
+    } else {
+      // 2, 3, 4 as these are the 3 newest, which is the only criteria we are
+      // using for this test.
+      EXPECT_EQ(ids[2], proto_report.sources(0).id());
+      EXPECT_EQ(ids[3], proto_report.sources(1).id());
+      EXPECT_EQ(ids[4], proto_report.sources(2).id());
+    }
+  }
+}
+
 }  // namespace ukm
diff --git a/components/viz/viz.gni b/components/viz/viz.gni
index 24dc56fb..c2652c0 100644
--- a/components/viz/viz.gni
+++ b/components/viz/viz.gni
@@ -12,7 +12,8 @@
 
 # Controls if GLRenderer related tests should be built and run. Platforms where
 # GLRenderer is no longer used (other than Linux) can stop running tests for it.
-enable_gl_renderer_tests = enable_gl_backend_tests && !is_android && !is_win
+enable_gl_renderer_tests =
+    enable_gl_backend_tests && !is_android && !is_win && !is_mac
 
 # TODO(samans): Support more configurations.
 # CFI issue: https://crbug.com/967819
diff --git a/content/browser/back_forward_cache_browsertest.cc b/content/browser/back_forward_cache_browsertest.cc
index 326279ad..8e83503 100644
--- a/content/browser/back_forward_cache_browsertest.cc
+++ b/content/browser/back_forward_cache_browsertest.cc
@@ -1998,15 +1998,9 @@
   }
 }
 
-// TODO(https://crbug.com/1275493): Flaky on Mac builders.
-#if defined(OS_MAC)
-#define MAYBE_SubframeTextInputStateUpdated \
-  DISABLED_SubframeTextInputStateUpdated
-#else
-#define MAYBE_SubframeTextInputStateUpdated SubframeTextInputStateUpdated
-#endif
+// TODO(https://crbug.com/1275493): Flaky on various builders.
 IN_PROC_BROWSER_TEST_F(BackForwardCacheBrowserTest,
-                       MAYBE_SubframeTextInputStateUpdated) {
+                       DISABLED_SubframeTextInputStateUpdated) {
   ASSERT_TRUE(embedded_test_server()->Start());
   GURL url_1(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b(a))"));
diff --git a/content/browser/direct_sockets/direct_sockets_open_browsertest.cc b/content/browser/direct_sockets/direct_sockets_open_browsertest.cc
index 6d6900a..89e22ed2 100644
--- a/content/browser/direct_sockets/direct_sockets_open_browsertest.cc
+++ b/content/browser/direct_sockets/direct_sockets_open_browsertest.cc
@@ -420,7 +420,7 @@
     base::HistogramTester histogram_tester;
     histogram_tester.ExpectBucketCount(
         kPermissionDeniedHistogramName,
-        DirectSocketsServiceImpl::FailureType::kResolvingToNonPublic, 0);
+        blink::mojom::DirectSocketFailureType::kResolvingToNonPublic, 0);
 
     const std::string script =
         base::StringPrintf("open%s({remoteAddress: '%s', remotePort: 993})",
@@ -429,7 +429,7 @@
     EXPECT_EQ(expected_result, EvalJs(shell(), script));
     histogram_tester.ExpectBucketCount(
         kPermissionDeniedHistogramName,
-        DirectSocketsServiceImpl::FailureType::kResolvingToNonPublic, 1);
+        blink::mojom::DirectSocketFailureType::kResolvingToNonPublic, 1);
   }
 
  protected:
@@ -476,16 +476,10 @@
   EXPECT_EQ(expected_result, EvalJs(shell(), script));
 }
 
-// TODO(crbug.com/1196515): Fix this flaky test.
 IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest,
-                       DISABLED_OpenTcp_TransientActivation) {
+                       OpenTcp_TransientActivation) {
   EXPECT_TRUE(NavigateToURL(shell(), GetTestOpenPageURL()));
 
-  base::HistogramTester histogram_tester;
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kTransientActivation, 0);
-
   MockNetworkContext mock_network_context(net::OK);
   DirectSocketsServiceImpl::SetNetworkContextForTesting(&mock_network_context);
 
@@ -493,11 +487,10 @@
       "openTcp({remoteAddress: '::1', remotePort: 993});\
        openTcp({remoteAddress: '::1', remotePort: 993})";
 
-  EXPECT_EQ("openTcp failed: NotAllowedError: Permission denied",
-            EvalJs(shell(), script));
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kTransientActivation, 1);
+  EXPECT_EQ(
+      "openTcp failed: NotAllowedError: Failed to execute 'openTCPSocket' on "
+      "'Navigator': Must be handling a user gesture to open a socket.",
+      EvalJs(shell(), script));
 }
 
 IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest, OpenTcp_CannotEvadeCors) {
@@ -506,7 +499,7 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kCORS, 0);
+      blink::mojom::DirectSocketFailureType::kCORS, 0);
 
   // HTTPS uses port 443.
   const std::string script =
@@ -516,7 +509,7 @@
             EvalJs(shell(), script));
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kCORS, 1);
+      blink::mojom::DirectSocketFailureType::kCORS, 1);
 }
 
 // Permission Denied failures(user dialog) should be triggered if connection
@@ -528,7 +521,7 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kUserDialog, 0);
+      blink::mojom::DirectSocketFailureType::kUserDialog, 0);
 
   DirectSocketsServiceImpl::SetConnectionDialogBypassForTesting(false);
 
@@ -539,7 +532,7 @@
             EvalJs(shell(), script));
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kUserDialog, 1);
+      blink::mojom::DirectSocketFailureType::kUserDialog, 1);
 }
 
 // Remote address should be provided or TEST will fail with NotAllowedError. In
@@ -562,7 +555,7 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 0);
+      blink::mojom::DirectSocketFailureType::kEnterprisePolicy, 0);
 
   DirectSocketsServiceImpl::SetEnterpriseManagedForTesting(true);
 
@@ -573,7 +566,7 @@
             EvalJs(shell(), script));
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 1);
+      blink::mojom::DirectSocketFailureType::kEnterprisePolicy, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest,
@@ -681,26 +674,19 @@
   EXPECT_EQ("openUdp succeeded", EvalJs(shell(), script));
 }
 
-// TODO(crbug.com/1213100): Fix this flaky test.
 IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest,
-                       DISABLED_OpenUdp_TransientActivation) {
+                       OpenUdp_TransientActivation) {
   EXPECT_TRUE(NavigateToURL(shell(), GetTestOpenPageURL()));
 
-  base::HistogramTester histogram_tester;
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kTransientActivation, 0);
-
   const std::string script = base::StringPrintf(
       "openUdp({remoteAddress: '127.0.0.1', remotePort: %d});\
        openUdp({remoteAddress: '127.0.0.1', remotePort: %d})",
       0, 0);
 
-  EXPECT_EQ("openUdp failed: NotAllowedError: Permission denied",
-            EvalJs(shell(), script));
-  histogram_tester.ExpectBucketCount(
-      kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kTransientActivation, 1);
+  EXPECT_EQ(
+      "openUdp failed: NotAllowedError: Failed to execute 'openUDPSocket' on "
+      "'Navigator': Must be handling a user gesture to open a socket.",
+      EvalJs(shell(), script));
 }
 
 IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest, OpenUdp_NotAllowedError) {
@@ -723,7 +709,7 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kCORS, 0);
+      blink::mojom::DirectSocketFailureType::kCORS, 0);
 
   // QUIC uses port 443.
   const std::string script =
@@ -733,7 +719,7 @@
             EvalJs(shell(), script));
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kCORS, 1);
+      blink::mojom::DirectSocketFailureType::kCORS, 1);
 }
 
 // Permission Denied failures(user dialog) should be triggered if connection
@@ -745,7 +731,7 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kUserDialog, 0);
+      blink::mojom::DirectSocketFailureType::kUserDialog, 0);
 
   DirectSocketsServiceImpl::SetConnectionDialogBypassForTesting(false);
 
@@ -756,7 +742,7 @@
             EvalJs(shell(), script));
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kUserDialog, 1);
+      blink::mojom::DirectSocketFailureType::kUserDialog, 1);
 }
 
 // Remote address should be provided or TEST will fail with NotAllowedError. In
@@ -779,7 +765,7 @@
   base::HistogramTester histogram_tester;
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 0);
+      blink::mojom::DirectSocketFailureType::kEnterprisePolicy, 0);
 
   DirectSocketsServiceImpl::SetEnterpriseManagedForTesting(true);
 
@@ -790,7 +776,7 @@
             EvalJs(shell(), script));
   histogram_tester.ExpectBucketCount(
       kPermissionDeniedHistogramName,
-      DirectSocketsServiceImpl::FailureType::kEnterprisePolicy, 1);
+      blink::mojom::DirectSocketFailureType::kEnterprisePolicy, 1);
 }
 
 IN_PROC_BROWSER_TEST_F(DirectSocketsOpenBrowserTest,
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.cc b/content/browser/direct_sockets/direct_sockets_service_impl.cc
index ad1f5034..8bb0e10e 100644
--- a/content/browser/direct_sockets/direct_sockets_service_impl.cc
+++ b/content/browser/direct_sockets/direct_sockets_service_impl.cc
@@ -39,6 +39,8 @@
 #include "chromeos/lacros/lacros_service.h"        // nogncheck
 #endif
 
+using blink::mojom::DirectSocketFailureType;
+
 namespace content {
 
 namespace {
@@ -115,7 +117,7 @@
 
   if (options.remote_port == 443) {
     base::UmaHistogramEnumeration(kPermissionDeniedHistogramName,
-                                  DirectSocketsServiceImpl::FailureType::kCORS);
+                                  DirectSocketFailureType::kCORS);
     // TODO(crbug.com/1119601): Issue a CORS preflight request.
     return net::ERR_UNSAFE_PORT;
   }
@@ -254,8 +256,9 @@
     if (!is_raw_address_ && !is_mdns_name_ && resolved_addresses &&
         ContainNonPubliclyRoutableAddress(*resolved_addresses)) {
       result = net::Error::ERR_NETWORK_ACCESS_DENIED;
-      base::UmaHistogramEnumeration(kPermissionDeniedHistogramName,
-                                    FailureType::kResolvingToNonPublic);
+      base::UmaHistogramEnumeration(
+          kPermissionDeniedHistogramName,
+          DirectSocketFailureType::kResolvingToNonPublic);
     }
     protocol_ == ProtocolType::kTcp ? OpenTCPSocket(result, resolved_addresses)
                                     : OpenUDPSocket(result, resolved_addresses);
@@ -526,21 +529,6 @@
   if (!IsAllowedRestrictedApiOrigin(frame_host_->GetLastCommittedOrigin()))
     return net::ERR_NETWORK_ACCESS_DENIED;
 
-  // TODO(crbug.com/1119600): Do not consume (or check) transient activation
-  // for reconnection attempts.
-  bool is_consumed =
-      static_cast<RenderFrameHostImpl*>(frame_host_)
-          ->frame_tree_node()
-          ->UpdateUserActivationState(
-              blink::mojom::UserActivationUpdateType::
-                  kConsumeTransientActivation,
-              blink::mojom::UserActivationNotificationType::kNone);
-  if (!is_consumed) {
-    base::UmaHistogramEnumeration(kPermissionDeniedHistogramName,
-                                  FailureType::kTransientActivation);
-    return net::ERR_NETWORK_ACCESS_DENIED;
-  }
-
   if (GetPermissionCallbackForTesting())
     return GetPermissionCallbackForTesting().Run(options);  // IN-TEST
 
@@ -551,7 +539,7 @@
   // policies are in effect.
   if (IsEnterpriseManaged()) {
     base::UmaHistogramEnumeration(kPermissionDeniedHistogramName,
-                                  FailureType::kEnterprisePolicy);
+                                  DirectSocketFailureType::kEnterprisePolicy);
     return net::ERR_NETWORK_ACCESS_DENIED;
   }
 
@@ -570,7 +558,7 @@
     const std::string& port) {
   if (!accepted && !g_connection_dialog_bypass_for_testing) {
     base::UmaHistogramEnumeration(kPermissionDeniedHistogramName,
-                                  FailureType::kUserDialog);
+                                  DirectSocketFailureType::kUserDialog);
     std::move(callback).Run(net::ERR_ABORTED, absl::nullopt, absl::nullopt,
                             mojo::ScopedDataPipeConsumerHandle(),
                             mojo::ScopedDataPipeProducerHandle());
@@ -607,7 +595,7 @@
     const std::string& port) {
   if (!accepted && !g_connection_dialog_bypass_for_testing) {
     base::UmaHistogramEnumeration(kPermissionDeniedHistogramName,
-                                  FailureType::kUserDialog);
+                                  DirectSocketFailureType::kUserDialog);
     std::move(callback).Run(net::ERR_ABORTED, absl::nullopt, absl::nullopt);
     return;
   }
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.h b/content/browser/direct_sockets/direct_sockets_service_impl.h
index 9a59ebc..956f51ea 100644
--- a/content/browser/direct_sockets/direct_sockets_service_impl.h
+++ b/content/browser/direct_sockets/direct_sockets_service_impl.h
@@ -32,19 +32,6 @@
     : public blink::mojom::DirectSocketsService,
       public WebContentsObserver {
  public:
-  // This enum is used to track how often each permission check cause
-  // Permission Denied failures.
-  enum class FailureType {
-    kPermissionsPolicy = 0,
-    kTransientActivation = 1,
-    kUserDialog = 2,
-    kResolvingToNonPublic = 3,
-    kRateLimiting = 4,
-    kCORS = 5,
-    kEnterprisePolicy = 6,
-    kMaxValue = kEnterprisePolicy,
-  };
-
   enum class ProtocolType { kTcp, kUdp };
 
   using PermissionCallback = base::RepeatingCallback<net::Error(
diff --git a/content/browser/renderer_host/navigation_controller_impl.cc b/content/browser/renderer_host/navigation_controller_impl.cc
index fe8ce9fe..5f71a5c 100644
--- a/content/browser/renderer_host/navigation_controller_impl.cc
+++ b/content/browser/renderer_host/navigation_controller_impl.cc
@@ -1263,7 +1263,7 @@
   }
 
   // Do navigation-type specific actions. These will make and commit an entry.
-  details->type = ClassifyNavigation(rfh, params, navigation_request);
+  details->type = ClassifyNavigation(rfh, params, navigation_request, details);
 
   // is_same_document must be computed before the entry gets committed.
   details->is_same_document = is_same_document_navigation;
@@ -1285,22 +1285,22 @@
     case NAVIGATION_TYPE_MAIN_FRAME_NEW_ENTRY:
       RendererDidNavigateToNewEntry(
           rfh, params, details->is_same_document, details->did_replace_entry,
-          previous_document_was_activated, navigation_request);
+          previous_document_was_activated, navigation_request, details);
       break;
     case NAVIGATION_TYPE_MAIN_FRAME_EXISTING_ENTRY:
       RendererDidNavigateToExistingEntry(rfh, params, details->is_same_document,
                                          was_restored, navigation_request,
-                                         keep_pending_entry);
+                                         keep_pending_entry, details);
       break;
     case NAVIGATION_TYPE_NEW_SUBFRAME:
       RendererDidNavigateNewSubframe(
           rfh, params, details->is_same_document, details->did_replace_entry,
-          previous_document_was_activated, navigation_request);
+          previous_document_was_activated, navigation_request, details);
       break;
     case NAVIGATION_TYPE_AUTO_SUBFRAME:
       if (!RendererDidNavigateAutoSubframe(
               rfh, params, details->is_same_document,
-              was_on_initial_empty_document, navigation_request)) {
+              was_on_initial_empty_document, navigation_request, details)) {
         // We don't send a notification about auto-subframe PageState during
         // UpdateStateForFrame, since it looks like nothing has changed.  Send
         // it here at commit time instead.
@@ -1419,10 +1419,38 @@
 NavigationType NavigationControllerImpl::ClassifyNavigation(
     RenderFrameHostImpl* rfh,
     const mojom::DidCommitProvisionalLoadParams& params,
-    NavigationRequest* navigation_request) {
+    NavigationRequest* navigation_request,
+    LoadCommittedDetails* details) {
+  if (GetLastCommittedEntry()->IsInitialEntry() &&
+      navigation_request->is_synchronous_renderer_commit() &&
+      !navigation_request->IsSameDocument() && !rfh->GetParent() &&
+      params.should_replace_current_entry) {
+    // This is the synchronous about:blank navigation that happens when calling
+    // window.open() with no URL, which used to not create a NavigationEntry
+    // when we have no NavigationEntry on FrameTree creation. We now have the
+    // initial NavigationEntry, but Android WebView still needs this info to
+    // ignore the NavigationStateChanged() call in some cases to avoid firing
+    // onPageFinished etc in more cases than it previously did.
+    // See also https://crbug.com/1277414.
+    details->used_to_be_ignored_due_to_null_navigation_entry = true;
+  }
+
   TraceReturnReason<tracing_category::kNavigation> trace_return(
       "ClassifyNavigation");
   DCHECK(GetLastCommittedEntry());
+  if (rfh->GetParent() &&
+      GetLastCommittedEntry()
+          ->did_not_exist_without_initial_navigation_entry()) {
+    // This is a subframe navigation on the initial empty document, which used
+    // to not have a NavigationEntry to attach to, or other similarly previously
+    // non-existent NavigationEntry. Now it can attach to the initial
+    // NavigationEntry (or other previously non-existent NavigationEntry), but
+    // Android WebView still needs this info to ignore the
+    // NavigationStateChanged() call in some cases to avoid firing
+    // onPageFinished etc in more cases than it previously did.
+    // See also https://crbug.com/1277414.
+    details->used_to_be_ignored_due_to_null_navigation_entry = true;
+  }
 
   if (params.did_create_new_entry) {
     // A new entry for either the main frame or a subframe.
@@ -1459,6 +1487,17 @@
     // history entry can result in change of SiteInstance. Check for such a case
     // here and classify it as NEW_ENTRY, as such navigations should be treated
     // as new with replacement.
+
+    if (GetLastCommittedEntry()
+            ->did_not_exist_without_initial_navigation_entry()) {
+      // This is a navigation that used to try to modify a non-existent
+      // NavigationEntry. Now a NavigationEntry should always exist for this
+      // navigation to modify, but Android WebView still needs this info to
+      // ignore the NavigationStateChanged() call in some cases to avoid firing
+      // onPageFinished etc in more cases than it previously did.
+      // See also https://crbug.com/1277414.
+      details->used_to_be_ignored_due_to_null_navigation_entry = true;
+    }
     trace_return.set_return_reason(
         "nav entry 0, last committed, existing entry");
     return NAVIGATION_TYPE_MAIN_FRAME_EXISTING_ENTRY;
@@ -1550,7 +1589,8 @@
     const mojom::DidCommitProvisionalLoadParams& params,
     NavigationRequest* request,
     NavigationEntryImpl::UpdatePolicy update_policy,
-    bool is_new_entry) {
+    bool is_new_entry,
+    LoadCommittedDetails* commit_details) {
   // Update the FrameNavigationEntry.
 
   std::vector<GURL> redirects;
@@ -1583,6 +1623,13 @@
   entry->set_page_type((request && request->DidEncounterError())
                            ? PAGE_TYPE_ERROR
                            : PAGE_TYPE_NORMAL);
+  if (commit_details) {
+    // If this navigation used to get ignored because there's no NavigationEntry
+    // for the FrameNavigationEntry to attach to, note the fact that the new
+    // NavigationEntry used to not exist.
+    entry->set_did_not_exist_without_initial_navigation_entry(
+        commit_details->used_to_be_ignored_due_to_null_navigation_entry);
+  }
   if (is_new_entry) {
     // Some properties of the NavigationEntry are only set when the entry is
     // new (we aren't reusing it).
@@ -1635,11 +1682,13 @@
       nullptr /* blob_url_loader_factory */, true /* is_initial_entry */);
   UpdateNavigationEntryDetails(
       new_entry.get(), rfh, *params, nullptr /* request */,
-      NavigationEntryImpl::UpdatePolicy::kUpdate, true /* is_new_entry */);
+      NavigationEntryImpl::UpdatePolicy::kUpdate, true /* is_new_entry */,
+      nullptr /* commit_details */);
+  new_entry->set_did_not_exist_without_initial_navigation_entry(true);
 
   InsertOrReplaceEntry(std::move(new_entry), false /* replace_entry */,
                        false /* was_post_commit_error */,
-                       is_in_fenced_frame_tree);
+                       is_in_fenced_frame_tree, nullptr /* commit_details */);
 }
 
 void NavigationControllerImpl::RendererDidNavigateToNewEntry(
@@ -1648,7 +1697,8 @@
     bool is_same_document,
     bool replace_entry,
     bool previous_document_was_activated,
-    NavigationRequest* request) {
+    NavigationRequest* request,
+    LoadCommittedDetails* commit_details) {
   std::unique_ptr<NavigationEntryImpl> new_entry;
   const absl::optional<url::Origin>& initiator_origin =
       request->common_params().initiator_origin;
@@ -1753,7 +1803,7 @@
   if (!request->IsPrerenderedPageActivation()) {
     UpdateNavigationEntryDetails(new_entry.get(), rfh, params, request,
                                  NavigationEntryImpl::UpdatePolicy::kUpdate,
-                                 true /* is_new_entry */);
+                                 true /* is_new_entry */, commit_details);
 
     // history.pushState() is classified as a navigation to a new page, but sets
     // is_same_document to true. In this case, we already have the title and
@@ -1789,7 +1839,8 @@
 
   InsertOrReplaceEntry(std::move(new_entry), replace_entry,
                        !request->post_commit_error_page_html().empty(),
-                       rfh->frame_tree_node()->IsInFencedFrameTree());
+                       rfh->frame_tree_node()->IsInFencedFrameTree(),
+                       commit_details);
 }
 
 void NavigationControllerImpl::RendererDidNavigateToExistingEntry(
@@ -1798,7 +1849,8 @@
     bool is_same_document,
     bool was_restored,
     NavigationRequest* request,
-    bool keep_pending_entry) {
+    bool keep_pending_entry,
+    LoadCommittedDetails* commit_details) {
   DCHECK(GetLastCommittedEntry()) << "ClassifyNavigation should guarantee "
                                   << "that a last committed entry exists.";
 
@@ -1902,7 +1954,7 @@
 
   UpdateNavigationEntryDetails(entry, rfh, params, request,
                                NavigationEntryImpl::UpdatePolicy::kUpdate,
-                               false /* is_new_entry */);
+                               false /* is_new_entry */, commit_details);
 
   // The redirected to page should not inherit the favicon from the previous
   // page.
@@ -1918,7 +1970,7 @@
   // Note that we need to use the "internal" version since we don't want to
   // actually change any other state, just kill the pointer.
   if (!keep_pending_entry)
-    DiscardNonCommittedEntries();
+    DiscardNonCommittedEntriesWithCommitDetails(commit_details);
 
   // Update the last committed index to reflect the committed entry.
   last_committed_entry_index_ = GetIndexOfEntry(entry);
@@ -1930,7 +1982,8 @@
     bool is_same_document,
     bool replace_entry,
     bool previous_document_was_activated,
-    NavigationRequest* request) {
+    NavigationRequest* request,
+    LoadCommittedDetails* commit_details) {
   DCHECK(ui::PageTransitionCoreTypeIs(params.transition,
                                       ui::PAGE_TRANSITION_MANUAL_SUBFRAME));
 
@@ -1975,13 +2028,20 @@
       replace_entry, previous_document_was_activated,
       request->IsRendererInitiated(), request->GetPreviousPageUkmSourceId());
 
+  // If this navigation used to get ignored because there's no NavigationEntry
+  // for the FrameNavigationEntry to attach to, note the fact that the new
+  // NavigationEntry used to not exist.
+  new_entry->set_did_not_exist_without_initial_navigation_entry(
+      commit_details->used_to_be_ignored_due_to_null_navigation_entry);
+
   // TODO(creis): Update this to add the frame_entry if we can't find the one
   // to replace, which can happen due to a unique name change. See
   // https://crbug.com/607205. For now, the call to CloneAndReplace() will
   // delete the |frame_entry| when the function exits if it doesn't get used.
 
   InsertOrReplaceEntry(std::move(new_entry), replace_entry, false,
-                       rfh->frame_tree_node()->IsInFencedFrameTree());
+                       rfh->frame_tree_node()->IsInFencedFrameTree(),
+                       commit_details);
 }
 
 bool NavigationControllerImpl::RendererDidNavigateAutoSubframe(
@@ -1989,7 +2049,8 @@
     const mojom::DidCommitProvisionalLoadParams& params,
     bool is_same_document,
     bool was_on_initial_empty_document,
-    NavigationRequest* request) {
+    NavigationRequest* request,
+    LoadCommittedDetails* commit_details) {
   DCHECK(ui::PageTransitionCoreTypeIs(params.transition,
                                       ui::PAGE_TRANSITION_AUTO_SUBFRAME));
 
@@ -2030,7 +2091,7 @@
       // We only need to discard the pending entry in this history navigation
       // case.  For newly created subframes, there was no pending entry.
       last_committed_entry_index_ = entry_index;
-      DiscardNonCommittedEntries();
+      DiscardNonCommittedEntriesWithCommitDetails(commit_details);
 
       // History navigations should send a commit notification.
       send_commit_notification = true;
@@ -2077,7 +2138,8 @@
   }
 
   UpdateNavigationEntryDetails(last_committed, rfh, params, request,
-                               update_policy, false /* is_new_entry */);
+                               update_policy, false /* is_new_entry */,
+                               commit_details);
 
   return send_commit_notification;
 }
@@ -2633,7 +2695,8 @@
     std::unique_ptr<NavigationEntryImpl> entry,
     bool replace,
     bool was_post_commit_error,
-    bool in_fenced_frame_tree) {
+    bool in_fenced_frame_tree,
+    LoadCommittedDetails* commit_details) {
   // Fenced frame trees should always have `ui::PAGE_TRANSITION_AUTO_SUBFRAME`
   // set because:
   // 1) They don't influence the history of the outer page.
@@ -2656,7 +2719,7 @@
   if (pending_entry_ && pending_entry_index_ == -1)
     entry->set_unique_id(pending_entry_->GetUniqueID());
 
-  DiscardNonCommittedEntries();
+  DiscardNonCommittedEntriesWithCommitDetails(commit_details);
 
   // When replacing, don't prune the forward history.
   if ((replace || was_post_commit_error) && entries_.size() > 0) {
@@ -3660,7 +3723,10 @@
   // when it wants to draw.  See http://crbug.com/11157
   ssl_manager_.DidCommitProvisionalLoad(*details);
 
-  delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
+  delegate_->NotifyNavigationStateChanged(
+      details->used_to_be_ignored_due_to_null_navigation_entry
+          ? INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+          : INVALIDATE_TYPE_ALL);
   delegate_->NotifyNavigationEntryCommitted(*details);
 
   // TODO(avi): Remove. http://crbug.com/170921
@@ -3789,6 +3855,11 @@
 }
 
 void NavigationControllerImpl::DiscardNonCommittedEntries() {
+  DiscardNonCommittedEntriesWithCommitDetails(nullptr /* commit_details */);
+}
+
+void NavigationControllerImpl::DiscardNonCommittedEntriesWithCommitDetails(
+    LoadCommittedDetails* commit_details) {
   // Avoid sending a notification if there is nothing to discard.
   // TODO(mthiesse): Temporarily checking failed_pending_entry_id_ to help
   // diagnose https://bugs.chromium.org/p/chromium/issues/detail?id=1007570.
@@ -3797,7 +3868,11 @@
   }
   DiscardPendingEntry(false);
   if (delegate_)
-    delegate_->NotifyNavigationStateChanged(INVALIDATE_TYPE_ALL);
+    delegate_->NotifyNavigationStateChanged(
+        (commit_details &&
+         commit_details->used_to_be_ignored_due_to_null_navigation_entry)
+            ? INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+            : INVALIDATE_TYPE_ALL);
 }
 
 int NavigationControllerImpl::GetEntryIndexWithUniqueID(
diff --git a/content/browser/renderer_host/navigation_controller_impl.h b/content/browser/renderer_host/navigation_controller_impl.h
index ff3e18a..6b9e9bb 100644
--- a/content/browser/renderer_host/navigation_controller_impl.h
+++ b/content/browser/renderer_host/navigation_controller_impl.h
@@ -153,6 +153,12 @@
   bool IsEntryMarkedToBeSkipped(int index) override;
   BackForwardCacheImpl& GetBackForwardCache() override;
 
+  // Discards the pending entry if any. If this is caused by a navigation
+  // committing a new entry, `commit_details` will contain the committed
+  // navigation's details.
+  void DiscardNonCommittedEntriesWithCommitDetails(
+      LoadCommittedDetails* commit_details);
+
   // Creates the initial NavigationEntry for the NavigationController when its
   // FrameTree is being initialized. See NavigationEntry::IsInitialEntry() on
   // what this means.
@@ -555,7 +561,8 @@
   NavigationType ClassifyNavigation(
       RenderFrameHostImpl* rfh,
       const mojom::DidCommitProvisionalLoadParams& params,
-      NavigationRequest* navigation_request);
+      NavigationRequest* navigation_request,
+      LoadCommittedDetails* load_committed_details);
 
   // Handlers for the different types of navigation types. They will actually
   // handle the navigations corresponding to the different NavClasses above.
@@ -577,27 +584,31 @@
       bool is_same_document,
       bool replace_entry,
       bool previous_document_was_activated,
-      NavigationRequest* request);
+      NavigationRequest* request,
+      LoadCommittedDetails* details);
   void RendererDidNavigateToExistingEntry(
       RenderFrameHostImpl* rfh,
       const mojom::DidCommitProvisionalLoadParams& params,
       bool is_same_document,
       bool was_restored,
       NavigationRequest* request,
-      bool keep_pending_entry);
+      bool keep_pending_entry,
+      LoadCommittedDetails* details);
   void RendererDidNavigateNewSubframe(
       RenderFrameHostImpl* rfh,
       const mojom::DidCommitProvisionalLoadParams& params,
       bool is_same_document,
       bool replace_entry,
       bool previous_document_was_activated,
-      NavigationRequest* request);
+      NavigationRequest* request,
+      LoadCommittedDetails* details);
   bool RendererDidNavigateAutoSubframe(
       RenderFrameHostImpl* rfh,
       const mojom::DidCommitProvisionalLoadParams& params,
       bool is_same_document,
       bool was_on_initial_empty_document,
-      NavigationRequest* request);
+      NavigationRequest* request,
+      LoadCommittedDetails* details);
 
   // Allows the derived class to issue notifications that a load has been
   // committed. This will fill in the active entry to the details structure.
@@ -620,7 +631,8 @@
   void InsertOrReplaceEntry(std::unique_ptr<NavigationEntryImpl> entry,
                             bool replace,
                             bool was_post_commit_error,
-                            bool is_in_fenced_frame_tree);
+                            bool is_in_fenced_frame_tree,
+                            LoadCommittedDetails* details);
 
   // Removes the entry at |index|, as long as it is not the current entry.
   void RemoveEntryAtIndexInternal(int index);
@@ -691,7 +703,8 @@
       const mojom::DidCommitProvisionalLoadParams& params,
       NavigationRequest* request,
       NavigationEntryImpl::UpdatePolicy update_policy,
-      bool is_new_entry);
+      bool is_new_entry,
+      LoadCommittedDetails* commit_details);
 
   // Broadcasts this controller's session history offset and length to all
   // renderers involved in rendering the current page. The offset is
diff --git a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
index e94bb2b2..1651c14a 100644
--- a/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
+++ b/content/browser/renderer_host/navigation_controller_impl_browsertest.cc
@@ -19406,6 +19406,294 @@
   EXPECT_EQ(0, observer.last_nav_entry_id());
   EXPECT_EQ(1, controller.GetEntryCount());
 }
+namespace {
+// Tracks how many NavigationStateChanged call were triggered with the target
+// flag.
+class NavigationStateChangedDelegate : public WebContentsDelegate {
+ public:
+  explicit NavigationStateChangedDelegate(InvalidateTypes target_flag)
+      : target_flag_(target_flag) {}
+
+  NavigationStateChangedDelegate(const NavigationStateChangedDelegate&) =
+      delete;
+  NavigationStateChangedDelegate& operator=(
+      const NavigationStateChangedDelegate&) = delete;
+
+  ~NavigationStateChangedDelegate() override = default;
+
+  // Keep track of whether the tab has notified us of a navigation state change
+  // which invalidates the displayed URL.
+  void NavigationStateChanged(WebContents* source,
+                              InvalidateTypes changed_flags) override {
+    if (changed_flags == target_flag_)
+      target_change_count_++;
+  }
+
+  int target_change_count() { return target_change_count_; }
+
+ private:
+  InvalidateTypes target_flag_;
+  int target_change_count_ = 0;
+};
+}  // namespace
+
+// Tests that committing iframes under the initial NavigationEntry dispatches
+// the expected NavigationStateChanged calls.
+IN_PROC_BROWSER_TEST_P(
+    NavigationControllerBrowserTest,
+    NavigationStateChangedForIframeUnderInitialNavigationEntry) {
+  GURL url1 = embedded_test_server()->GetURL("/title1.html");
+  GURL url2 = embedded_test_server()->GetURL("/title2.html");
+  GURL url3 = embedded_test_server()->GetURL("/title3.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+  FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
+  // Pop open a new window to a URL that never commits.
+  ShellAddedObserver new_shell_observer;
+  EXPECT_TRUE(ExecJs(root, "var w = window.open('/nocontent')"));
+  Shell* new_shell = new_shell_observer.GetShell();
+  WebContentsImpl* new_contents =
+      static_cast<WebContentsImpl*>(new_shell->web_contents());
+  EXPECT_TRUE(WaitForLoadStop(new_contents));
+
+  // Observe NavigationStateChanged calls with the flag
+  // INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY.
+  NavigationStateChangedDelegate changed_all_but_ignored_delegate(
+      INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY);
+  new_contents->SetDelegate(&changed_all_but_ignored_delegate);
+  EXPECT_EQ(0, changed_all_but_ignored_delegate.target_change_count());
+  ASSERT_NE(new_contents, shell()->web_contents());
+  FrameTreeNode* new_root = new_contents->GetPrimaryFrameTree().root();
+
+  NavigationControllerImpl& controller = new_contents->GetController();
+  // The new window is on the initial NavigationEntry.
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_TRUE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+
+  {
+    // Make a new iframe in the popup.
+    LoadCommittedCapturer capturer(new_contents);
+    std::string script = JsReplace(kAddFrameWithSrcScript, url1);
+    EXPECT_TRUE(ExecJs(new_root, script));
+    capturer.Wait();
+    EXPECT_EQ(url1, new_root->child_at(0)->current_url());
+  }
+  // The new window is still on the initial NavigationEntry.
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_TRUE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+  // No new NavigationEntry was committed, so no
+  // INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+  // NavigationStateChanged call was triggered.
+  EXPECT_EQ(0, changed_all_but_ignored_delegate.target_change_count());
+
+  {
+    //  Navigate the iframe to another URL.
+    FrameNavigateParamsCapturer capturer(new_root->child_at(0));
+    NavigateFrameToURL(new_root->child_at(0), url2);
+    capturer.Wait();
+    EXPECT_EQ(url2, new_root->child_at(0)->current_url());
+    EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.navigation_type());
+  }
+
+  // The new window is no longer on the initial NavigationEntry because a new
+  // NavigationEntry was committed.
+  EXPECT_EQ(2, controller.GetEntryCount());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  // We are no longer on the initial NavigationEntry but the last navigation
+  // used to be ignored before initial NavigationEntry exists, because there is
+  // no NavigationEntry for the iframe's new FrameNavigationEntry to attach to.
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+
+  // 2 INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+  // NavigationStateChanged calls were triggered;
+  // #1 was triggered by DiscardNonCommittedEntries().
+  // #2 is triggered by NotifyNavigationEntryCommitted().
+  EXPECT_EQ(2, changed_all_but_ignored_delegate.target_change_count());
+
+  {
+    //  Navigate the iframe to another URL again.
+    FrameNavigateParamsCapturer capturer(new_root->child_at(0));
+    NavigateFrameToURL(new_root->child_at(0), url3);
+    capturer.Wait();
+    EXPECT_EQ(url3, new_root->child_at(0)->current_url());
+    EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.navigation_type());
+  }
+
+  // A new NavigationEntry was committed.
+  EXPECT_EQ(3, controller.GetEntryCount());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  // Again, the last navigation used to be ignored before initial
+  // NavigationEntry exists, because there is no NavigationEntry for the
+  // iframe's new FrameNavigationEntry to attach to.
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+
+  // 2 INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+  // NavigationStateChanged calls were triggered again:
+  // #1 was triggered by DiscardNonCommittedEntries().
+  // #2 is triggered by NotifyNavigationEntryCommitted().
+  EXPECT_EQ(4, changed_all_but_ignored_delegate.target_change_count());
+}
+
+// Tests that committing iframes under the synchronously committed about:blank
+// document created for window.open() with no URL dispatches the expected
+// NavigationStateChanged calls.
+IN_PROC_BROWSER_TEST_P(NavigationControllerBrowserTest,
+                       NavigationStateChangedForIframeUnderEmptURLyWindowOpen) {
+  GURL url1 = embedded_test_server()->GetURL("/title1.html");
+  GURL url2 = embedded_test_server()->GetURL("/title2.html");
+  GURL url3 = embedded_test_server()->GetURL("/title3.html");
+  EXPECT_TRUE(NavigateToURL(shell(), url1));
+  FrameTreeNode* root = contents()->GetPrimaryFrameTree().root();
+  // Pop open a new window with window.open() (without specifying an URL).
+  ShellAddedObserver new_shell_observer;
+  EXPECT_TRUE(ExecJs(root, "var w = window.open()"));
+  Shell* new_shell = new_shell_observer.GetShell();
+  WebContentsImpl* new_contents =
+      static_cast<WebContentsImpl*>(new_shell->web_contents());
+  EXPECT_TRUE(WaitForLoadStop(new_contents));
+
+  // Observe NavigationStateChanged calls with the flag
+  // INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY.
+  NavigationStateChangedDelegate changed_all_but_ignored_delegate(
+      INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY);
+  new_contents->SetDelegate(&changed_all_but_ignored_delegate);
+  EXPECT_EQ(0, changed_all_but_ignored_delegate.target_change_count());
+  ASSERT_NE(new_contents, shell()->web_contents());
+  FrameTreeNode* new_root = new_contents->GetPrimaryFrameTree().root();
+
+  NavigationControllerImpl& controller = new_contents->GetController();
+  // The new window is not on the initial NavigationEntry because of the
+  // synchronous about:blank navigation.
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  // Before the initial NavigationEntry was introduced, the synchronous
+  // about:blank navigation for the empty-URL window.open() used to get ignored,
+  // so its NavigationEntry used to not exist.
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+
+  {
+    // Make a new iframe in the popup.
+    LoadCommittedCapturer capturer(new_contents);
+    std::string script = JsReplace(kAddFrameWithSrcScript, url1);
+    EXPECT_TRUE(ExecJs(new_root, script));
+    capturer.Wait();
+    EXPECT_EQ(url1, new_root->child_at(0)->current_url());
+  }
+  // The new window is on the synchronous about:blank NavigationEntry, which
+  // replaced the initial NavigationEntry.
+  EXPECT_EQ(1, controller.GetEntryCount());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+  // No new NavigationEntry was committed, so no
+  // INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+  // NavigationStateChanged call was triggered.
+  EXPECT_EQ(0, changed_all_but_ignored_delegate.target_change_count());
+
+  {
+    //  Navigate the iframe to another URL.
+    FrameNavigateParamsCapturer capturer(new_root->child_at(0));
+    NavigateFrameToURL(new_root->child_at(0), url2);
+    capturer.Wait();
+    EXPECT_EQ(url2, new_root->child_at(0)->current_url());
+    EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.navigation_type());
+  }
+
+  // The new window is no longer on the initial NavigationEntry because a new
+  // NavigationEntry was committed.
+  EXPECT_EQ(2, controller.GetEntryCount());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  // The last navigation used to be ignored before initial NavigationEntry
+  // exists, because there is no NavigationEntry for the iframe's new
+  // FrameNavigationEntry to attach to.
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+
+  // 2 INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+  // NavigationStateChanged calls were triggered;
+  // #1 was triggered by DiscardNonCommittedEntries().
+  // #2 is triggered by NotifyNavigationEntryCommitted().
+  EXPECT_EQ(2, changed_all_but_ignored_delegate.target_change_count());
+
+  {
+    //  Navigate the iframe to another URL again.
+    FrameNavigateParamsCapturer capturer(new_root->child_at(0));
+    NavigateFrameToURL(new_root->child_at(0), url3);
+    capturer.Wait();
+    EXPECT_EQ(url3, new_root->child_at(0)->current_url());
+    EXPECT_EQ(NAVIGATION_TYPE_NEW_SUBFRAME, capturer.navigation_type());
+  }
+
+  // A new NavigationEntry was committed.
+  EXPECT_EQ(3, controller.GetEntryCount());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  // Again, the last navigation used to be ignored before initial
+  // NavigationEntry exists, because there is no NavigationEntry for the
+  // iframe's new FrameNavigationEntry to attach to.
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+
+  // 2 INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+  // NavigationStateChanged calls were triggered again:
+  // #1 was triggered by DiscardNonCommittedEntries().
+  // #2 is triggered by NotifyNavigationEntryCommitted().
+  EXPECT_EQ(4, changed_all_but_ignored_delegate.target_change_count());
+
+  {
+    // Navigate the popup main frame to a same-document URL.
+    // The navigation happens on the initial empty document, so it replaced the
+    // last committed entry.
+    FrameNavigateParamsCapturer capturer(new_root);
+    EXPECT_TRUE(ExecJs(new_root, "location.href = '#foo';"));
+    capturer.Wait();
+    EXPECT_EQ(GURL("about:blank#foo"), new_root->current_url());
+    EXPECT_EQ(NAVIGATION_TYPE_MAIN_FRAME_EXISTING_ENTRY,
+              capturer.navigation_type());
+    EXPECT_TRUE(capturer.is_same_document());
+    EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+
+    // The navigation happens on the initial empty document, so it replaced the
+    // last committed entry.
+    EXPECT_TRUE(capturer.did_replace_entry());
+    EXPECT_EQ(3, controller.GetEntryCount());
+  }
+
+  // The new NavigationEntry is still marked as "used to not exist" as it was
+  // modifying the existing navigation entry.
+  EXPECT_TRUE(controller.GetLastCommittedEntry()
+                  ->did_not_exist_without_initial_navigation_entry());
+  // 1 INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY
+  // NavigationStateChanged call were triggered by
+  // NotifyNavigationEntryCommitted().
+  EXPECT_EQ(5, changed_all_but_ignored_delegate.target_change_count());
+
+  {
+    // Navigate the popup main frame to another URL.
+    FrameNavigateParamsCapturer capturer(new_root);
+    EXPECT_TRUE(ExecJs(new_root, JsReplace("location.href = $1;", url2)));
+    capturer.Wait();
+    EXPECT_EQ(url2, new_root->current_url());
+    EXPECT_EQ(NAVIGATION_TYPE_MAIN_FRAME_NEW_ENTRY, capturer.navigation_type());
+    EXPECT_TRUE(capturer.did_replace_entry());
+  }
+
+  // The navigation happens on the "initial empty document", so it replaced the
+  // last committed entry.
+  EXPECT_EQ(3, controller.GetEntryCount());
+  EXPECT_FALSE(controller.GetLastCommittedEntry()->IsInitialEntry());
+  // The new NavigationEntry is no longer marked as "used to not exist" as the
+  // latest navigation is not a navigation that used to get ignored before the
+  // introduction of the initial NavigationEntry.
+  EXPECT_FALSE(controller.GetLastCommittedEntry()
+                   ->did_not_exist_without_initial_navigation_entry());
+  EXPECT_EQ(5, changed_all_but_ignored_delegate.target_change_count());
+}
 
 INSTANTIATE_TEST_SUITE_P(
     All,
diff --git a/content/browser/renderer_host/navigation_entry_impl.h b/content/browser/renderer_host/navigation_entry_impl.h
index 5653ee8..88c1740 100644
--- a/content/browser/renderer_host/navigation_entry_impl.h
+++ b/content/browser/renderer_host/navigation_entry_impl.h
@@ -434,6 +434,16 @@
     is_initial_entry_ = is_initial_entry;
   }
 
+  void set_did_not_exist_without_initial_navigation_entry(
+      bool did_not_exist_without_initial_navigation_entry) {
+    did_not_exist_without_initial_navigation_entry_ =
+        did_not_exist_without_initial_navigation_entry;
+  }
+
+  bool did_not_exist_without_initial_navigation_entry() {
+    return did_not_exist_without_initial_navigation_entry_;
+  }
+
  private:
   std::unique_ptr<NavigationEntryImpl> CloneAndReplaceInternal(
       scoped_refptr<FrameNavigationEntry> frame_entry,
@@ -576,6 +586,19 @@
   // restore, because we never restore tabs with only the initial
   // NavigationEntry.
   bool is_initial_entry_ = false;
+
+  // True if this NavigationEntry used to not exist before the introduction of
+  // the initial NavigationEntry (see above). This will be true for initial
+  // NavigationEntries, but also for any NavigationEntry that are copied from
+  // the initial NavigationEntry, such as when a subframe navigation uses or
+  // copies the initial NavigationEntry to attach to. This is also true for the
+  // NavigationEntry created after the synchronous about:blank navigation for
+  // window.open() with no URL, which used to get ignored as the browser doesn't
+  // know what to do with it. This information is needed for Android WebView to
+  // ignore the NavigationStateChanged() call in some cases to avoid firing
+  // onPageFinished etc in more cases than it previously did.
+  // See also https://crbug.com/1277414.
+  bool did_not_exist_without_initial_navigation_entry_ = false;
 };
 
 }  // namespace content
diff --git a/content/browser/renderer_host/navigation_request.cc b/content/browser/renderer_host/navigation_request.cc
index 5dd95be..02440a91 100644
--- a/content/browser/renderer_host/navigation_request.cc
+++ b/content/browser/renderer_host/navigation_request.cc
@@ -1319,6 +1319,8 @@
   navigation_request->coep_reporter_ = std::move(coep_reporter);
   navigation_request->isolation_info_for_subresources_ =
       isolation_info_for_subresources;
+  navigation_request->associated_site_instance_type_ =
+      AssociatedSiteInstanceType::CURRENT;
   navigation_request->StartNavigation();
   DCHECK(navigation_request->IsNavigationStarted());
 
@@ -2146,6 +2148,14 @@
     common_params_->should_replace_current_entry = true;
   }
 
+  DCHECK_NE(AssociatedSiteInstanceType::NONE, associated_site_instance_type_);
+  RenderFrameHostImpl* navigating_frame_host =
+      associated_site_instance_type_ == AssociatedSiteInstanceType::SPECULATIVE
+          ? frame_tree_node_->render_manager()->speculative_frame_host()
+          : frame_tree_node_->current_frame_host();
+  DCHECK(navigating_frame_host);
+  SetExpectedProcess(navigating_frame_host->GetProcess());
+
   DCHECK(!IsNavigationStarted());
   SetState(WILL_START_REQUEST);
   is_navigation_started_ = true;
@@ -3713,8 +3723,6 @@
           : frame_tree_node_->current_frame_host();
   DCHECK(navigating_frame_host);
 
-  SetExpectedProcess(navigating_frame_host->GetProcess());
-
   BrowserContext* browser_context =
       frame_tree_node_->navigator().controller().GetBrowserContext();
   StoragePartition* partition = browser_context->GetStoragePartition(
@@ -6524,6 +6532,11 @@
   return previous_render_frame_host_id_;
 }
 
+int NavigationRequest::GetExpectedRenderProcessHostId() {
+  DCHECK_LT(state_, READY_TO_COMMIT);
+  return expected_render_process_host_id_;
+}
+
 bool NavigationRequest::IsServedFromBackForwardCache() {
   const NavigationRequest& request = *this;
   return request.IsServedFromBackForwardCache();
diff --git a/content/browser/renderer_host/navigation_request.h b/content/browser/renderer_host/navigation_request.h
index bfe2dae..89db156 100644
--- a/content/browser/renderer_host/navigation_request.h
+++ b/content/browser/renderer_host/navigation_request.h
@@ -361,6 +361,7 @@
   void RegisterSubresourceOverride(
       blink::mojom::TransferrableURLLoaderPtr transferrable_loader) override;
   GlobalRenderFrameHostId GetPreviousRenderFrameHostId() override;
+  int GetExpectedRenderProcessHostId() override;
   bool IsServedFromBackForwardCache() override;
   void SetIsOverridingUserAgent(bool override_ua) override;
   void SetSilentlyIgnoreErrors() override;
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index ee2df90..4a9baa42 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -99,7 +99,6 @@
 #include "content/public/common/content_features.h"
 #include "content/public/common/content_switches.h"
 #include "content/public/common/url_constants.h"
-#include "content/public/common/use_zoom_for_dsf_policy.h"
 #include "content/public/test/back_forward_cache_util.h"
 #include "content/public/test/browser_test.h"
 #include "content/public/test/browser_test_utils.h"
@@ -120,7 +119,6 @@
 #include "content/test/content_browser_test_utils_internal.h"
 #include "content/test/did_commit_navigation_interceptor.h"
 #include "content/test/render_document_feature.h"
-#include "content/test/render_widget_host_visibility_observer.h"
 #include "content/test/test_content_browser_client.h"
 #include "ipc/constants.mojom.h"
 #include "ipc/ipc_security_test_util.h"
@@ -169,12 +167,6 @@
 #if defined(USE_AURA)
 #include "content/browser/renderer_host/render_widget_host_view_aura.h"
 #include "ui/aura/window.h"
-#include "ui/aura/window_tree_host.h"
-#endif
-
-#if defined(OS_MAC)
-#include "content/browser/renderer_host/input/synthetic_touchpad_pinch_gesture.h"
-#include "ui/base/test/scoped_preferred_scroller_style_mac.h"
 #endif
 
 #if defined(OS_ANDROID)
@@ -197,10 +189,6 @@
 #include "ui/gfx/geometry/point_f.h"
 #endif
 
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-#include "ui/aura/test/test_screen.h"
-#endif
-
 using ::testing::SizeIs;
 using ::testing::WhenSorted;
 using ::testing::ElementsAre;
@@ -265,10 +253,6 @@
   return EvalJs(ftn, "self.origin;");
 }
 
-double GetFrameDeviceScaleFactor(const ToRenderFrameHost& adapter) {
-  return EvalJs(adapter, "window.devicePixelRatio;").ExtractDouble();
-}
-
 class RedirectNotificationObserver : public NotificationObserver {
  public:
   // Register to listen for notifications of the given type from either a
@@ -502,34 +486,6 @@
             node->current_frame_host()->GetProcess()->GetFrameDepth());
 }
 
-// Check |intersects_viewport| on widget and process.
-bool CheckIntersectsViewport(bool expected, FrameTreeNode* node) {
-  RenderProcessHost::Priority priority =
-      node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
-  return priority.intersects_viewport == expected &&
-         node->current_frame_host()->GetProcess()->GetIntersectsViewport() ==
-             expected;
-}
-
-// Layout child frames in cross_site_iframe_factory.html so that they are the
-// same width as the viewport, and 75% of the height of the window. This is for
-// testing viewport intersection. Note this does not recurse into child frames
-// and re-layout in the same way since children might be in a different origin.
-void LayoutNonRecursiveForTestingViewportIntersection(
-    const ToRenderFrameHost& execution_target) {
-  static const char kRafScript[] = R"(
-      let width = window.innerWidth;
-      let height = window.innerHeight * 0.75;
-      for (let i = 0; i < window.frames.length; i++) {
-        let child = document.getElementById("child-" + i);
-        child.width = width;
-        child.height = height;
-      }
-  )";
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(execution_target, kRafScript, "")
-                  .error.empty());
-}
-
 void GenerateTapDownGesture(RenderWidgetHost* rwh) {
   blink::WebGestureEvent gesture_tap_down(
       blink::WebGestureEvent::Type::kGestureTapDown,
@@ -542,66 +498,6 @@
 
 }  // namespace
 
-// Class to monitor incoming UpdateViewportIntersection messages.
-class UpdateViewportIntersectionMessageFilter
-    : public blink::mojom::RemoteFrameHostInterceptorForTesting {
- public:
-  explicit UpdateViewportIntersectionMessageFilter(
-      content::RenderFrameProxyHost* rfph)
-      : intersection_state_(blink::mojom::ViewportIntersectionState::New()),
-        render_frame_proxy_host_(rfph) {
-    render_frame_proxy_host_->frame_host_receiver_for_testing()
-        .SwapImplForTesting(this);
-  }
-
-  const blink::mojom::ViewportIntersectionStatePtr& GetIntersectionState()
-      const {
-    return intersection_state_;
-  }
-
-  RenderFrameProxyHost* GetForwardingInterface() override {
-    return render_frame_proxy_host_;
-  }
-
-  void UpdateViewportIntersection(
-      blink::mojom::ViewportIntersectionStatePtr intersection_state,
-      const absl::optional<blink::FrameVisualProperties>& visual_properties)
-      override {
-    intersection_state_ = std::move(intersection_state);
-    msg_received_ = true;
-    if (run_loop_)
-      run_loop_->Quit();
-  }
-
-  bool MessageReceived() const { return msg_received_; }
-
-  void Clear() {
-    msg_received_ = false;
-    intersection_state_ = blink::mojom::ViewportIntersectionState::New();
-  }
-
-  void Wait() {
-    DCHECK(!run_loop_);
-    if (msg_received_) {
-      msg_received_ = false;
-      return;
-    }
-    std::unique_ptr<base::RunLoop> run_loop(new base::RunLoop);
-    run_loop_ = run_loop.get();
-    run_loop_->Run();
-    run_loop_ = nullptr;
-    msg_received_ = false;
-  }
-
-  void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
-
- private:
-  raw_ptr<base::RunLoop> run_loop_ = nullptr;
-  bool msg_received_;
-  blink::mojom::ViewportIntersectionStatePtr intersection_state_;
-  raw_ptr<content::RenderFrameProxyHost> render_frame_proxy_host_;
-};
-
 //
 // SitePerProcessBrowserTestBase
 //
@@ -660,23 +556,6 @@
   return url::Origin::Create(url).Serialize();
 }
 
-// SitePerProcessHighDPIBrowserTest
-
-class SitePerProcessHighDPIBrowserTest : public SitePerProcessBrowserTest {
- public:
-  const double kDeviceScaleFactor = 2.0;
-
-  SitePerProcessHighDPIBrowserTest() = default;
-
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(
-        switches::kForceDeviceScaleFactor,
-        base::StringPrintf("%f", kDeviceScaleFactor));
-  }
-};
-
 // SitePerProcessIgnoreCertErrorsBrowserTest
 
 void SitePerProcessIgnoreCertErrorsBrowserTest::SetUpOnMainThread() {
@@ -726,299 +605,6 @@
   }
 };
 
-IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIBrowserTest,
-                       SubframeLoadsWithCorrectDeviceScaleFactor) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // On Android forcing device scale factor does not work for tests, therefore
-  // we ensure that make frame and iframe have the same DIP scale there, but
-  // not necessarily kDeviceScaleFactor.
-  const double expected_dip_scale =
-#if defined(OS_ANDROID)
-      GetFrameDeviceScaleFactor(web_contents());
-#else
-      SitePerProcessHighDPIBrowserTest::kDeviceScaleFactor;
-#endif
-
-  EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(web_contents()));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(root));
-  ASSERT_EQ(1U, root->child_count());
-
-  FrameTreeNode* child = root->child_at(0);
-  EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(child));
-}
-
-#if BUILDFLAG(IS_CHROMEOS_ASH)
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       SubframeUpdateToCorrectDeviceScaleFactor) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  EXPECT_EQ(1.0, GetFrameDeviceScaleFactor(web_contents()));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-
-  FrameTreeNode* child = root->child_at(0);
-  EXPECT_EQ(1.0, GetFrameDeviceScaleFactor(child));
-
-  double expected_dip_scale = 2.0;
-
-  // TODO(oshima): allow DeviceScaleFactor change on other platforms
-  // (win, linux, mac, android and mus).
-  aura::TestScreen* test_screen =
-      static_cast<aura::TestScreen*>(display::Screen::GetScreen());
-  test_screen->CreateHostForPrimaryDisplay();
-  test_screen->SetDeviceScaleFactor(expected_dip_scale);
-
-  // This forces |expected_dip_scale| to be applied to the aura::WindowTreeHost
-  // and aura::Window.
-  aura::WindowTreeHost* window_tree_host = shell()->window()->GetHost();
-  window_tree_host->SetBoundsInPixels(window_tree_host->GetBoundsInPixels());
-
-  // Wait until dppx becomes 2 if the frame's dpr hasn't beeen updated
-  // to 2 yet.
-  const char kScript[] =
-      "function sendDpr() "
-      "{window.domAutomationController.send(window.devicePixelRatio);}; "
-      "if (window.devicePixelRatio == 2) sendDpr();"
-      "window.matchMedia('screen and "
-      "(min-resolution: 2dppx)').addListener(function(e) { if (e.matches) { "
-      "sendDpr();}})";
-  // Make sure that both main frame and iframe are updated to 2x.
-  EXPECT_EQ(expected_dip_scale,
-            EvalJs(child, kScript, content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
-                .ExtractDouble());
-
-  EXPECT_EQ(expected_dip_scale, EvalJs(web_contents(), kScript,
-                                       content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
-                                    .ExtractDouble());
-}
-
-#endif
-
-class TextAutosizerPageInfoInterceptor
-    : public blink::mojom::LocalMainFrameHostInterceptorForTesting {
- public:
-  explicit TextAutosizerPageInfoInterceptor(
-      RenderFrameHostImpl* render_frame_host)
-      : render_frame_host_(render_frame_host) {
-    render_frame_host_->local_main_frame_host_receiver_for_testing()
-        .SwapImplForTesting(this);
-  }
-
-  ~TextAutosizerPageInfoInterceptor() override = default;
-
-  LocalMainFrameHost* GetForwardingInterface() override {
-    return render_frame_host_;
-  }
-
-  void WaitForPageInfo(absl::optional<int> target_main_frame_width,
-                       absl::optional<float> target_device_scale_adjustment) {
-    if (remote_page_info_seen_)
-      return;
-    target_main_frame_width_ = target_main_frame_width;
-    target_device_scale_adjustment_ = target_device_scale_adjustment;
-    run_loop_ = std::make_unique<base::RunLoop>();
-    run_loop_->Run();
-    run_loop_.reset();
-  }
-
-  const blink::mojom::TextAutosizerPageInfo& GetTextAutosizerPageInfo() {
-    return *remote_page_info_;
-  }
-
-  void TextAutosizerPageInfoChanged(
-      blink::mojom::TextAutosizerPageInfoPtr remote_page_info) override {
-    if ((!target_main_frame_width_ ||
-         remote_page_info->main_frame_width != target_main_frame_width_) &&
-        (!target_device_scale_adjustment_ ||
-         remote_page_info->device_scale_adjustment !=
-             target_device_scale_adjustment_)) {
-      return;
-    }
-    remote_page_info_ = remote_page_info.Clone();
-    remote_page_info_seen_ = true;
-    if (run_loop_)
-      run_loop_->Quit();
-    GetForwardingInterface()->TextAutosizerPageInfoChanged(
-        std::move(remote_page_info));
-  }
-
- private:
-  raw_ptr<RenderFrameHostImpl> render_frame_host_;
-  bool remote_page_info_seen_ = false;
-  blink::mojom::TextAutosizerPageInfoPtr remote_page_info_ =
-      blink::mojom::TextAutosizerPageInfo::New(/*main_frame_width=*/0,
-                                               /*main_frame_layout_width=*/0,
-                                               /*device_scale_adjustment=*/1.f);
-  std::unique_ptr<base::RunLoop> run_loop_;
-  absl::optional<int> target_main_frame_width_;
-  absl::optional<float> target_device_scale_adjustment_;
-};
-
-// TODO(tonikitoo): Move to fake_remote_frame.h|cc in case it is useful
-// for other tests.
-class FakeRemoteMainFrame : public blink::mojom::RemoteMainFrame {
- public:
-  FakeRemoteMainFrame() = default;
-  ~FakeRemoteMainFrame() override = default;
-
-  void Init(
-      mojo::PendingAssociatedReceiver<blink::mojom::RemoteMainFrame> receiver) {
-    receiver_.Bind(std::move(receiver));
-  }
-
-  // blink::mojom::RemoteMainFrame overrides:
-  void UpdateTextAutosizerPageInfo(
-      blink::mojom::TextAutosizerPageInfoPtr page_info) override {}
-
- private:
-  mojo::AssociatedReceiver<blink::mojom::RemoteMainFrame> receiver_{this};
-};
-
-// This class intercepts RenderFrameProxyHost creations, and overrides their
-// respective blink::mojom::RemoteMainFrame instances, so that it can watch for
-// text autosizer page info updates.
-class UpdateTextAutosizerInfoProxyObserver
-    : public RenderFrameProxyHost::TestObserver {
- public:
-  UpdateTextAutosizerInfoProxyObserver() {
-    RenderFrameProxyHost::SetObserverForTesting(this);
-  }
-  ~UpdateTextAutosizerInfoProxyObserver() override {
-    RenderFrameProxyHost::SetObserverForTesting(nullptr);
-  }
-
-  const blink::mojom::TextAutosizerPageInfo& TextAutosizerPageInfo(
-      RenderFrameProxyHost* proxy) {
-    return remote_frames_[proxy]->page_info();
-  }
-
- private:
-  class Remote : public FakeRemoteMainFrame {
-   public:
-    explicit Remote(RenderFrameProxyHost* proxy) {
-      Init(proxy->BindRemoteMainFrameReceiverForTesting());
-    }
-    void UpdateTextAutosizerPageInfo(
-        blink::mojom::TextAutosizerPageInfoPtr page_info) override {
-      page_info_ = *page_info;
-    }
-    const blink::mojom::TextAutosizerPageInfo& page_info() {
-      return page_info_;
-    }
-
-   private:
-    blink::mojom::TextAutosizerPageInfo page_info_;
-  };
-
-  void OnRemoteMainFrameBound(RenderFrameProxyHost* proxy_host) override {
-    remote_frames_[proxy_host] = std::make_unique<Remote>(proxy_host);
-  }
-
-  std::map<RenderFrameProxyHost*, std::unique_ptr<Remote>> remote_frames_;
-};
-
-// Make sure that when a relevant feature of the main frame changes, e.g. the
-// frame width, that the browser is notified.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TextAutosizerPageInfo) {
-  UpdateTextAutosizerInfoProxyObserver update_text_autosizer_info_observer;
-
-  blink::web_pref::WebPreferences prefs =
-      web_contents()->GetOrCreateWebPreferences();
-  prefs.text_autosizing_enabled = true;
-
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  web_contents()->SetWebPreferences(prefs);
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* b_child = root->child_at(0);
-
-  blink::mojom::TextAutosizerPageInfo received_page_info;
-  auto interceptor = std::make_unique<TextAutosizerPageInfoInterceptor>(
-      web_contents()->GetMainFrame());
-#if defined(OS_ANDROID)
-  prefs.device_scale_adjustment += 0.05f;
-  // Change the device scale adjustment to trigger a RemotePageInfo update.
-  web_contents()->SetWebPreferences(prefs);
-  // Make sure we receive a ViewHostMsg from the main frame's renderer.
-  interceptor->WaitForPageInfo(absl::optional<int>(),
-                               prefs.device_scale_adjustment);
-  // Make sure the correct page message is sent to the child.
-  base::RunLoop().RunUntilIdle();
-  received_page_info = interceptor->GetTextAutosizerPageInfo();
-  EXPECT_EQ(prefs.device_scale_adjustment,
-            received_page_info.device_scale_adjustment);
-#else
-  // Resize the main frame, then wait to observe that the RemotePageInfo message
-  // arrives.
-  auto* view = web_contents()->GetRenderWidgetHostView();
-  gfx::Rect old_bounds = view->GetViewBounds();
-  gfx::Rect new_bounds(
-      old_bounds.origin(),
-      gfx::Size(old_bounds.width() - 20, old_bounds.height() - 20));
-
-  view->SetBounds(new_bounds);
-  // Make sure we receive a ViewHostMsg from the main frame's renderer.
-  interceptor->WaitForPageInfo(new_bounds.width(), absl::optional<float>());
-  // Make sure the correct page message is sent to the child.
-  base::RunLoop().RunUntilIdle();
-  received_page_info = interceptor->GetTextAutosizerPageInfo();
-  EXPECT_EQ(new_bounds.width(), received_page_info.main_frame_width);
-#endif  // defined(OS_ANDROID)
-
-  // Dynamically create a new, cross-process frame to test sending the cached
-  // TextAutosizerPageInfo.
-
-  GURL c_url = embedded_test_server()->GetURL("c.com", "/title1.html");
-  // The following is a hack so we can get an IPC watcher connected to the
-  // RenderProcessHost for C before the RenderView is created for it, and the
-  // TextAutosizerPageInfo IPC is sent to it.
-  scoped_refptr<SiteInstance> c_site =
-      web_contents()->GetSiteInstance()->GetRelatedSiteInstance(c_url);
-  // Force creation of a render process for c's SiteInstance, this will get
-  // used when we dynamically create the new frame.
-  auto* c_rph = static_cast<RenderProcessHostImpl*>(c_site->GetProcess());
-  ASSERT_TRUE(c_rph);
-  ASSERT_NE(c_rph, root->current_frame_host()->GetProcess());
-  ASSERT_NE(c_rph, b_child->current_frame_host()->GetProcess());
-
-  // Create the subframe now.
-  std::string create_frame_script = base::StringPrintf(
-      "var new_iframe = document.createElement('iframe');"
-      "new_iframe.src = '%s';"
-      "document.body.appendChild(new_iframe);",
-      c_url.spec().c_str());
-  EXPECT_TRUE(ExecJs(root, create_frame_script));
-  ASSERT_EQ(2U, root->child_count());
-
-  // Ensure IPC is sent.
-  base::RunLoop().RunUntilIdle();
-  blink::mojom::TextAutosizerPageInfo page_info_sent_to_remote_main_frames =
-      update_text_autosizer_info_observer.TextAutosizerPageInfo(
-          web_contents()
-              ->GetRenderManager()
-              ->GetAllProxyHostsForTesting()
-              .begin()
-              ->second.get());
-
-  EXPECT_EQ(received_page_info.main_frame_width,
-            page_info_sent_to_remote_main_frames.main_frame_width);
-  EXPECT_EQ(received_page_info.main_frame_layout_width,
-            page_info_sent_to_remote_main_frames.main_frame_layout_width);
-  EXPECT_EQ(received_page_info.device_scale_adjustment,
-            page_info_sent_to_remote_main_frames.device_scale_adjustment);
-}
-
 // Ensure that navigating subframes in --site-per-process mode works and the
 // correct documents are committed.
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CrossSiteIframe) {
@@ -1188,155 +774,6 @@
   EXPECT_EQ(expected_title, entry->GetTitle());
 }
 
-// Test that the physical backing size and view bounds for a scaled out-of-
-// process iframe are set and updated correctly.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       CompositorViewportPixelSizeTest) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_scaled_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  ASSERT_EQ(1U, root->child_count());
-
-  FrameTreeNode* parent_iframe_node = root->child_at(0);
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site A ------- proxies for B\n"
-      "        +--Site B -- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://baz.com/",
-      DepictFrameTree(root));
-
-  FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
-  RenderFrameProxyHost* proxy_to_parent =
-      nested_iframe_node->render_manager()->GetProxyToParent();
-  CrossProcessFrameConnector* connector =
-      proxy_to_parent->cross_process_frame_connector();
-  RenderWidgetHostViewBase* rwhv_nested =
-      static_cast<RenderWidgetHostViewBase*>(
-          nested_iframe_node->current_frame_host()
-              ->GetRenderWidgetHost()
-              ->GetView());
-
-  RenderFrameSubmissionObserver frame_observer(nested_iframe_node);
-  frame_observer.WaitForMetadataChange();
-
-  // Verify that applying a CSS scale transform does not impact the size of the
-  // content of the nested iframe.
-  // The screen_space_rect_in_dip may be off by 1 due to rounding. There is no
-  // good way to avoid this due to various device-scale-factor. (e.g. when
-  // dsf=3.375, ceil(round(50 * 3.375) / 3.375) = 51. Thus, we allow the screen
-  // size in dip to be off by 1 here.
-  EXPECT_NEAR(50, connector->screen_space_rect_in_dip().size().width(), 1);
-  EXPECT_NEAR(50, connector->screen_space_rect_in_dip().size().height(), 1);
-  EXPECT_EQ(gfx::Size(100, 100), rwhv_nested->GetViewBounds().size());
-  EXPECT_EQ(gfx::Size(100, 100), connector->local_frame_size_in_dip());
-  EXPECT_EQ(connector->local_frame_size_in_pixels(),
-            rwhv_nested->GetCompositorViewportPixelSize());
-}
-
-// Verify an OOPIF resize handler doesn't fire immediately after load without
-// the frame having been resized. See https://crbug.com/826457.
-// TODO(crbug.com/1278038): Test is very flaky on many platforms.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       DISABLED_NoResizeAfterIframeLoad) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(a)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  FrameTreeNode* iframe = root->child_at(0);
-  GURL site_url =
-      embedded_test_server()->GetURL("b.com", "/page_with_resize_handler.html");
-  EXPECT_TRUE(NavigateToURLFromRenderer(iframe, site_url));
-  base::RunLoop().RunUntilIdle();
-
-  // Should be zero because the iframe only has its initial size from parent.
-  EXPECT_EQ(0, EvalJs(iframe->current_frame_host(), "resize_count;"));
-}
-
-// Test that the view bounds for an out-of-process iframe are set and updated
-// correctly, including accounting for local frame offsets in the parent and
-// scroll positions.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ViewBoundsInNestedFrameTest) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(a)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-  RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
-      root->current_frame_host()->GetRenderWidgetHost()->GetView());
-  ASSERT_EQ(1U, root->child_count());
-
-  FrameTreeNode* parent_iframe_node = root->child_at(0);
-  GURL site_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_positioned_frame.html"));
-  EXPECT_TRUE(NavigateToURLFromRenderer(parent_iframe_node, site_url));
-  RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site A ------- proxies for B\n"
-      "        +--Site B -- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://baz.com/",
-      DepictFrameTree(root));
-
-  FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
-  RenderWidgetHostViewBase* rwhv_nested =
-      static_cast<RenderWidgetHostViewBase*>(
-          nested_iframe_node->current_frame_host()
-              ->GetRenderWidgetHost()
-              ->GetView());
-  WaitForHitTestData(nested_iframe_node->current_frame_host());
-
-  float scale_factor =
-      frame_observer.LastRenderFrameMetadata().page_scale_factor;
-
-  // Get the view bounds of the nested iframe, which should account for the
-  // relative offset of its direct parent within the root frame.
-  gfx::Rect bounds = rwhv_nested->GetViewBounds();
-
-  RenderFrameProxyHost* parent_iframe_proxy =
-      nested_iframe_node->render_manager()->GetProxyToParent();
-  auto interceptor = std::make_unique<SynchronizeVisualPropertiesInterceptor>(
-      parent_iframe_proxy);
-
-  // Scroll the parent frame downward to verify that the child rect gets updated
-  // correctly.
-  blink::WebMouseWheelEvent scroll_event(
-      blink::WebInputEvent::Type::kMouseWheel,
-      blink::WebInputEvent::kNoModifiers,
-      blink::WebInputEvent::GetStaticTimeStampForTests());
-
-  scroll_event.SetPositionInWidget(
-      std::floor((bounds.x() - rwhv_root->GetViewBounds().x() - 5) *
-                 scale_factor),
-      std::floor((bounds.y() - rwhv_root->GetViewBounds().y() - 5) *
-                 scale_factor));
-  scroll_event.delta_x = 0.0f;
-  scroll_event.delta_y = -30.0f;
-  scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
-  rwhv_root->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
-  interceptor->WaitForRect();
-
-  // The precise amount of scroll for the first view position update is not
-  // deterministic, so this simply verifies that the OOPIF moved from its
-  // earlier position.
-  gfx::Rect update_rect = interceptor->last_rect();
-  EXPECT_LT(update_rect.y(), bounds.y() - rwhv_root->GetViewBounds().y());
-}
-
 // This test verifies that scroll bubbling from an OOPIF properly forwards
 // GestureFlingStart events from the child frame to the parent frame. This
 // test times out on failure.
@@ -2862,206 +2299,6 @@
   }
 }
 
-// Verify that "scrolling" property on frame elements propagates to child frames
-// correctly.
-// Does not work on android since android has scrollbars overlayed.
-// TODO(bokan): Pretty soon most/all platforms will use overlay scrollbars. This
-// test should find a better way to check for scrollability. crbug.com/662196.
-// Flaky on Linux. crbug.com/790929.
-#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
-#define MAYBE_FrameOwnerPropertiesPropagationScrolling \
-  DISABLED_FrameOwnerPropertiesPropagationScrolling
-#else
-#define MAYBE_FrameOwnerPropertiesPropagationScrolling \
-  FrameOwnerPropertiesPropagationScrolling
-#endif
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       MAYBE_FrameOwnerPropertiesPropagationScrolling) {
-#if defined(OS_MAC)
-  ui::test::ScopedPreferredScrollerStyle scroller_style_override(false);
-#endif
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_owner_properties_scrolling.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1u, root->child_count());
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site B ------- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/",
-      DepictFrameTree(root));
-
-  FrameTreeNode* child = root->child_at(0);
-
-  // If the available client width within the iframe is smaller than the
-  // frame element's width, we assume there's a scrollbar.
-  // Also note that just comparing clientHeight and scrollHeight of the frame's
-  // document will not work.
-  auto has_scrollbar = [](RenderFrameHostImpl* rfh) {
-    int client_width = EvalJs(rfh, "document.body.clientWidth").ExtractInt();
-    const int kFrameElementWidth = 200;
-    return client_width < kFrameElementWidth;
-  };
-
-  auto set_scrolling_property = [](RenderFrameHostImpl* parent_rfh,
-                                   const std::string& value) {
-    EXPECT_TRUE(ExecJs(
-        parent_rfh,
-        base::StringPrintf("document.getElementById('child-1').setAttribute("
-                           "    'scrolling', '%s');",
-                           value.c_str())));
-  };
-
-  // Run the test over variety of parent/child cases.
-  GURL urls[] = {// Remote to remote.
-                 embedded_test_server()->GetURL("c.com", "/tall_page.html"),
-                 // Remote to local.
-                 embedded_test_server()->GetURL("a.com", "/tall_page.html"),
-                 // Local to remote.
-                 embedded_test_server()->GetURL("b.com", "/tall_page.html")};
-  const std::string scrolling_values[] = {"yes", "auto", "no"};
-
-  for (size_t i = 0; i < base::size(scrolling_values); ++i) {
-    bool expect_scrollbar = scrolling_values[i] != "no";
-    set_scrolling_property(root->current_frame_host(), scrolling_values[i]);
-    for (size_t j = 0; j < base::size(urls); ++j) {
-      EXPECT_TRUE(NavigateToURLFromRenderer(child, urls[j]));
-      EXPECT_EQ(expect_scrollbar, has_scrollbar(child->current_frame_host()));
-    }
-  }
-}
-
-// Verify that "marginwidth" and "marginheight" properties on frame elements
-// propagate to child frames correctly.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       FrameOwnerPropertiesPropagationMargin) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_owner_properties_margin.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1u, root->child_count());
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site B ------- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/",
-      DepictFrameTree(root));
-
-  FrameTreeNode* child = root->child_at(0);
-
-  EXPECT_EQ("10", EvalJs(child, "document.body.getAttribute('marginwidth');"));
-  EXPECT_EQ("50", EvalJs(child, "document.body.getAttribute('marginheight');"));
-
-  // Run the test over variety of parent/child cases.
-  GURL urls[] = {// Remote to remote.
-                 embedded_test_server()->GetURL("c.com", "/title2.html"),
-                 // Remote to local.
-                 embedded_test_server()->GetURL("a.com", "/title1.html"),
-                 // Local to remote.
-                 embedded_test_server()->GetURL("b.com", "/title2.html")};
-
-  int current_margin_width = 15;
-  int current_margin_height = 25;
-
-  // Before each navigation, we change the marginwidth and marginheight
-  // properties of the frame. We then check whether those properties are applied
-  // correctly after the navigation has completed.
-  for (size_t i = 0; i < base::size(urls); ++i) {
-    // Change marginwidth and marginheight before navigating.
-    EXPECT_TRUE(ExecJs(
-        root,
-        base::StringPrintf("var child = document.getElementById('child-1');"
-                           "child.setAttribute('marginwidth', '%d');"
-                           "child.setAttribute('marginheight', '%d');",
-                           current_margin_width, current_margin_height)));
-
-    EXPECT_TRUE(NavigateToURLFromRenderer(child, urls[i]));
-
-    EXPECT_EQ(base::NumberToString(current_margin_width),
-              EvalJs(child, "document.body.getAttribute('marginwidth');"));
-    EXPECT_EQ(base::NumberToString(current_margin_height),
-              EvalJs(child, "document.body.getAttribute('marginheight');"));
-
-    current_margin_width += 5;
-    current_margin_height += 10;
-  }
-}
-
-// Verify that "csp" property on frame elements propagates to child frames
-// correctly. See https://crbug.com/647588
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       FrameOwnerPropertiesPropagationCSP) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_owner_properties_csp.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1u, root->child_count());
-
-  // The document in the iframe is blocked by CSPEE. An error page is loaded, it
-  // stays in the process of the main document.
-  EXPECT_EQ(
-      " Site A\n"
-      "   +--Site A\n"
-      "Where A = http://a.com/",
-      DepictFrameTree(root));
-
-  FrameTreeNode* child = root->child_at(0);
-
-  EXPECT_EQ(
-      "object-src \'none\'",
-      EvalJs(root, "document.getElementById('child-1').getAttribute('csp');"));
-
-  // Run the test over variety of parent/child cases.
-  struct {
-    std::string csp_value;
-    GURL url;
-    bool should_block;
-  } testCases[]{
-      // Remote to remote.
-      {"default-src a.com",
-       embedded_test_server()->GetURL("c.com", "/title2.html"), true},
-      // Remote to local.
-      {"default-src b.com",
-       embedded_test_server()->GetURL("a.com", "/title1.html"), false},
-      // Local to remote.
-      {"img-src c.com", embedded_test_server()->GetURL("b.com", "/title2.html"),
-       true},
-  };
-
-  // Before each navigation, we change the csp property of the frame.
-  // We then check whether that property is applied
-  // correctly after the navigation has completed.
-  for (const auto& testCase : testCases) {
-    // Change csp before navigating.
-    EXPECT_TRUE(ExecJs(
-        root,
-        base::StringPrintf("document.getElementById('child-1').setAttribute("
-                           "    'csp', '%s');",
-                           testCase.csp_value.c_str())));
-
-    NavigateFrameToURL(child, testCase.url);
-    EXPECT_EQ(testCase.csp_value, child->csp_attribute()->header->header_value);
-    // TODO(amalika): add checks that the CSP replication takes effect
-
-    const url::Origin child_origin =
-        child->current_frame_host()->GetLastCommittedOrigin();
-
-    EXPECT_EQ(testCase.should_block, child_origin.opaque());
-    EXPECT_EQ(url::Origin::Create(testCase.url.DeprecatedGetOriginAsURL())
-                  .GetTupleOrPrecursorTupleIfOpaque(),
-              child_origin.GetTupleOrPrecursorTupleIfOpaque());
-  }
-}
-
 // Verify origin replication with an A-embed-B-embed-C-embed-A hierarchy.
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, OriginReplication) {
   GURL main_url(embedded_test_server()->GetURL(
@@ -5273,263 +4510,6 @@
   EXPECT_TRUE(watcher.did_exit_normally());
 }
 
-// This test verifies that changing the CSS visibility of a cross-origin
-// <iframe> is forwarded to its corresponding RenderWidgetHost and all other
-// RenderWidgetHosts corresponding to the nested cross-origin frame.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CSSVisibilityChanged) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b(b(c(d(d(a))))))"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // Find all child RenderWidgetHosts.
-  std::vector<RenderWidgetHostImpl*> child_widget_hosts;
-  FrameTreeNode* first_cross_process_child =
-      web_contents()->GetPrimaryFrameTree().root()->child_at(0);
-  for (auto* ftn : web_contents()->GetPrimaryFrameTree().SubtreeNodes(
-           first_cross_process_child)) {
-    RenderFrameHostImpl* frame_host = ftn->current_frame_host();
-    if (!frame_host->is_local_root())
-      continue;
-
-    child_widget_hosts.push_back(frame_host->GetRenderWidgetHost());
-  }
-
-  // Ignoring the root, there is exactly 4 local roots and hence 5
-  // RenderWidgetHosts on the page.
-  EXPECT_EQ(4U, child_widget_hosts.size());
-
-  // Initially all the RenderWidgetHosts should be visible.
-  for (size_t index = 0; index < child_widget_hosts.size(); ++index) {
-    EXPECT_FALSE(child_widget_hosts[index]->is_hidden())
-        << "The RWH at distance " << index + 1U
-        << " from root RWH should not be hidden.";
-  }
-
-  std::string show_script =
-      "document.querySelector('iframe').style.visibility = 'visible';";
-  std::string hide_script =
-      "document.querySelector('iframe').style.visibility = 'hidden';";
-
-  // Define observers for notifications about hiding child RenderWidgetHosts.
-  std::vector<std::unique_ptr<RenderWidgetHostVisibilityObserver>>
-      hide_widget_host_observers(child_widget_hosts.size());
-  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
-    hide_widget_host_observers[index] =
-        std::make_unique<RenderWidgetHostVisibilityObserver>(
-            child_widget_hosts[index], false);
-  }
-
-  EXPECT_TRUE(ExecJs(shell(), hide_script));
-  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
-    EXPECT_TRUE(hide_widget_host_observers[index]->WaitUntilSatisfied())
-        << "Expected RenderWidgetHost at distance " << index + 1U
-        << " from root RenderWidgetHost to become hidden.";
-  }
-
-  // Define observers for notifications about showing child RenderWidgetHosts.
-  std::vector<std::unique_ptr<RenderWidgetHostVisibilityObserver>>
-      show_widget_host_observers(child_widget_hosts.size());
-  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
-    show_widget_host_observers[index] =
-        std::make_unique<RenderWidgetHostVisibilityObserver>(
-            child_widget_hosts[index], true);
-  }
-
-  EXPECT_TRUE(ExecJs(shell(), show_script));
-  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
-    EXPECT_TRUE(show_widget_host_observers[index]->WaitUntilSatisfied())
-        << "Expected RenderWidgetHost at distance " << index + 1U
-        << " from root RenderWidgetHost to become shown.";
-  }
-}
-
-// This test verifies that hiding an OOPIF in CSS will stop generating
-// compositor frames for the OOPIF and any nested OOPIFs inside it. This holds
-// even when the whole page is shown.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       HiddenOOPIFWillNotGenerateCompositorFrames) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_two_frames.html"));
-  ASSERT_TRUE(NavigateToURL(shell(), main_url));
-  ASSERT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
-
-  GURL cross_site_url_b =
-      embedded_test_server()->GetURL("b.com", "/counter.html");
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url_b));
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), cross_site_url_b));
-
-  // Now inject code in the first frame to create a nested OOPIF.
-  RenderFrameHostCreatedObserver new_frame_created_observer(
-      shell()->web_contents(), 1);
-  ASSERT_TRUE(
-      ExecJs(root->child_at(0)->current_frame_host(),
-             "document.body.appendChild(document.createElement('iframe'));"));
-  new_frame_created_observer.Wait();
-
-  GURL cross_site_url_a =
-      embedded_test_server()->GetURL("a.com", "/counter.html");
-
-  // Navigate the nested frame.
-  TestFrameNavigationObserver observer(root->child_at(0)->child_at(0));
-  ASSERT_TRUE(ExecJs(root->child_at(0)->current_frame_host(),
-                     JsReplace("document.querySelector('iframe').src = $1",
-                               cross_site_url_a)));
-  observer.Wait();
-
-  RenderWidgetHostViewChildFrame* first_child_view =
-      static_cast<RenderWidgetHostViewChildFrame*>(
-          root->child_at(0)->current_frame_host()->GetView());
-  RenderWidgetHostViewChildFrame* second_child_view =
-      static_cast<RenderWidgetHostViewChildFrame*>(
-          root->child_at(1)->current_frame_host()->GetView());
-  RenderWidgetHostViewChildFrame* nested_child_view =
-      static_cast<RenderWidgetHostViewChildFrame*>(
-          root->child_at(0)->child_at(0)->current_frame_host()->GetView());
-
-  RenderFrameSubmissionObserver first_frame_counter(
-      first_child_view->host_->render_frame_metadata_provider());
-  RenderFrameSubmissionObserver second_frame_counter(
-      second_child_view->host_->render_frame_metadata_provider());
-  RenderFrameSubmissionObserver third_frame_counter(
-      nested_child_view->host_->render_frame_metadata_provider());
-
-  const int kFrameCountLimit = 20;
-
-  // Wait for a minimum number of compositor frames for the second frame.
-  while (second_frame_counter.render_frame_count() < kFrameCountLimit)
-    second_frame_counter.WaitForAnyFrameSubmission();
-  ASSERT_LE(kFrameCountLimit, second_frame_counter.render_frame_count());
-
-  // Now make sure all frames have roughly the counter value in the sense that
-  // no counter value is more than twice any other.
-  float ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
-                static_cast<float>(second_frame_counter.render_frame_count());
-  EXPECT_GT(2.5f, ratio + 1 / ratio) << "Ratio is: " << ratio;
-
-  ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
-          static_cast<float>(third_frame_counter.render_frame_count());
-  EXPECT_GT(2.5f, ratio + 1 / ratio) << "Ratio is: " << ratio;
-
-  // Make sure all views can become visible.
-  EXPECT_TRUE(first_child_view->CanBecomeVisible());
-  EXPECT_TRUE(second_child_view->CanBecomeVisible());
-  EXPECT_TRUE(nested_child_view->CanBecomeVisible());
-
-  // Hide the first frame and wait for the notification to be posted by its
-  // RenderWidgetHost.
-  RenderWidgetHostVisibilityObserver hide_observer(
-      root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), false);
-
-  // Hide the first frame.
-  ASSERT_TRUE(ExecJs(
-      shell(),
-      "document.getElementsByName('frame1')[0].style.visibility = 'hidden'"));
-  ASSERT_TRUE(hide_observer.WaitUntilSatisfied());
-  EXPECT_TRUE(first_child_view->FrameConnectorForTesting()->IsHidden());
-
-  // Verify that only the second view can become visible now.
-  EXPECT_FALSE(first_child_view->CanBecomeVisible());
-  EXPECT_TRUE(second_child_view->CanBecomeVisible());
-  EXPECT_FALSE(nested_child_view->CanBecomeVisible());
-
-  // Now hide and show the WebContents (to simulate a tab switch).
-  shell()->web_contents()->WasHidden();
-  shell()->web_contents()->WasShown();
-
-  first_frame_counter.ResetCounter();
-  second_frame_counter.ResetCounter();
-  third_frame_counter.ResetCounter();
-
-  // We expect the second counter to keep running.
-  while (second_frame_counter.render_frame_count() < kFrameCountLimit)
-    second_frame_counter.WaitForAnyFrameSubmission();
-  ASSERT_LT(kFrameCountLimit, second_frame_counter.render_frame_count() + 1);
-
-  // Verify that the counter for other two frames did not count much.
-  ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
-          static_cast<float>(second_frame_counter.render_frame_count());
-  EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
-
-  ratio = static_cast<float>(third_frame_counter.render_frame_count()) /
-          static_cast<float>(second_frame_counter.render_frame_count());
-  EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
-}
-
-// This test verifies that navigating a hidden OOPIF to cross-origin will not
-// lead to creating compositor frames for the new OOPIF renderer.
-IN_PROC_BROWSER_TEST_P(
-    SitePerProcessBrowserTest,
-    HiddenOOPIFWillNotGenerateCompositorFramesAfterNavigation) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_two_frames.html"));
-  ASSERT_TRUE(NavigateToURL(shell(), main_url));
-  ASSERT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
-
-  GURL cross_site_url_b =
-      embedded_test_server()->GetURL("b.com", "/counter.html");
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url_b));
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), cross_site_url_b));
-
-  // Hide the first frame and wait for the notification to be posted by its
-  // RenderWidgetHost.
-  RenderWidgetHostVisibilityObserver hide_observer(
-      root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), false);
-
-  // Hide the first frame.
-  ASSERT_TRUE(ExecJs(
-      shell(),
-      "document.getElementsByName('frame1')[0].style.visibility = 'hidden'"));
-  ASSERT_TRUE(hide_observer.WaitUntilSatisfied());
-
-  // Now navigate the first frame to another OOPIF process.
-  TestFrameNavigationObserver navigation_observer(
-      root->child_at(0)->current_frame_host());
-  GURL cross_site_url_c =
-      embedded_test_server()->GetURL("c.com", "/counter.html");
-  ASSERT_TRUE(
-      ExecJs(web_contents(),
-             JsReplace("document.getElementsByName('frame1')[0].src = $1",
-                       cross_site_url_c)));
-  navigation_observer.Wait();
-
-  // Now investigate compositor frame creation.
-  RenderWidgetHostViewChildFrame* first_child_view =
-      static_cast<RenderWidgetHostViewChildFrame*>(
-          root->child_at(0)->current_frame_host()->GetView());
-
-  RenderWidgetHostViewChildFrame* second_child_view =
-      static_cast<RenderWidgetHostViewChildFrame*>(
-          root->child_at(1)->current_frame_host()->GetView());
-
-  EXPECT_FALSE(first_child_view->CanBecomeVisible());
-
-  RenderFrameSubmissionObserver first_frame_counter(
-      first_child_view->host_->render_frame_metadata_provider());
-  RenderFrameSubmissionObserver second_frame_counter(
-      second_child_view->host_->render_frame_metadata_provider());
-
-  const int kFrameCountLimit = 20;
-
-  // Wait for a certain number of swapped compositor frames generated for the
-  // second child view. During the same interval the first frame should not have
-  // swapped any compositor frames.
-  while (second_frame_counter.render_frame_count() < kFrameCountLimit)
-    second_frame_counter.WaitForAnyFrameSubmission();
-  ASSERT_LT(kFrameCountLimit, second_frame_counter.render_frame_count() + 1);
-
-  float ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
-                static_cast<float>(second_frame_counter.render_frame_count());
-  EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
-}
-
 // Verify that sandbox flags inheritance works across multiple levels of
 // frames.  See https://crbug.com/576845.
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, SandboxFlagsInheritance) {
@@ -6181,25 +5161,6 @@
                     .content_security_policies.size());
 }
 
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ScreenCoordinates) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  FrameTreeNode* child = root->child_at(0);
-
-  const char* properties[] = {"screenX", "screenY", "outerWidth",
-                              "outerHeight"};
-
-  for (const char* property : properties) {
-    std::string script = base::StringPrintf("window.%s;", property);
-    int root_value = EvalJs(root, script).ExtractInt();
-    int child_value = EvalJs(child, script).ExtractInt();
-    EXPECT_EQ(root_value, child_value);
-  }
-}
-
 // Tests that the state of the RenderViewHost is properly reset when the main
 // frame is navigated to the same SiteInstance as one of its child frames.
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
@@ -7129,38 +6090,6 @@
                    "document.getElementById('fileinput').files[0].name;"));
 }
 
-// Tests that an out-of-process iframe receives the visibilitychange event.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, VisibilityChange) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site B ------- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/",
-      DepictFrameTree(root));
-
-  EXPECT_TRUE(
-      ExecJs(root->child_at(0),
-             "var event_fired = 0;\n"
-             "document.addEventListener('visibilitychange',\n"
-             "                          function() { event_fired++; });\n"));
-
-  shell()->web_contents()->WasHidden();
-
-  EXPECT_EQ(1, EvalJs(root->child_at(0), "event_fired"));
-
-  shell()->web_contents()->WasShown();
-
-  EXPECT_EQ(2, EvalJs(root->child_at(0), "event_fired"));
-}
-
 // Test that the pending RenderFrameHost is canceled and destroyed when its
 // process dies. Previously, reusing a top-level pending RFH which
 // is not live was hitting a CHECK in CreateRenderView due to having neither a
@@ -10544,113 +9473,6 @@
   child_gesture_tap_ack_waiter.Wait();
 }
 
-// This test verifies that the main-frame's page scale factor propagates to
-// the compositor layertrees in each of the child processes.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       PageScaleFactorPropagatesToOOPIFs) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(2u, root->child_count());
-  FrameTreeNode* child_b = root->child_at(0);
-  FrameTreeNode* child_c = root->child_at(1);
-  ASSERT_EQ(1U, child_b->child_count());
-  FrameTreeNode* child_d = child_b->child_at(0);
-
-  ASSERT_TRUE(child_b);
-  ASSERT_TRUE(child_c);
-  ASSERT_TRUE(child_d);
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C D\n"
-      "   |--Site B ------- proxies for A C D\n"
-      "   |    +--Site C -- proxies for A B D\n"
-      "   +--Site D ------- proxies for A B C\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/\n"
-      "      C = http://c.com/\n"
-      "      D = http://d.com/",
-      DepictFrameTree(root));
-
-  RenderFrameSubmissionObserver observer_a(root);
-  RenderFrameSubmissionObserver observer_b(child_b);
-  RenderFrameSubmissionObserver observer_c(child_c);
-  RenderFrameSubmissionObserver observer_d(child_d);
-
-  // Monitor visual sync messages coming from the mainframe to make sure
-  // |is_pinch_gesture_active| goes true during the pinch gesture.
-  RenderFrameProxyHost* root_proxy_host =
-      child_d->render_manager()->GetProxyToParent();
-  auto interceptor_mainframe =
-      std::make_unique<SynchronizeVisualPropertiesInterceptor>(root_proxy_host);
-
-  // Monitor frame sync messages coming from child_b as it will need to
-  // relay them to child_d.
-  RenderFrameProxyHost* child_b_proxy_host =
-      child_c->render_manager()->GetProxyToParent();
-  auto interceptor_child_b =
-      std::make_unique<SynchronizeVisualPropertiesInterceptor>(
-          child_b_proxy_host);
-
-  // We need to observe a root frame submission to pick up the initial page
-  // scale factor.
-  observer_a.WaitForAnyFrameSubmission();
-
-  const float kPageScaleDelta = 2.f;
-  // On desktop systems we expect |current_page_scale| to be 1.f, but on
-  // Android it will typically be less than 1.f, and may take on arbitrary
-  // values.
-  float current_page_scale =
-      observer_a.LastRenderFrameMetadata().page_scale_factor;
-  float target_page_scale = current_page_scale * kPageScaleDelta;
-
-  SyntheticPinchGestureParams params;
-  auto* host = static_cast<RenderWidgetHostImpl*>(
-      root->current_frame_host()->GetRenderWidgetHost());
-  gfx::Rect bounds(host->GetView()->GetViewBounds().size());
-  // The synthetic gesture code expects a location in root-view coordinates.
-  params.anchor = gfx::PointF(bounds.CenterPoint());
-  // In SyntheticPinchGestureParams, |scale_factor| is really a delta.
-  params.scale_factor = kPageScaleDelta;
-#if defined(OS_MAC)
-  auto synthetic_pinch_gesture =
-      std::make_unique<SyntheticTouchpadPinchGesture>(params);
-#else
-  auto synthetic_pinch_gesture =
-      std::make_unique<SyntheticTouchscreenPinchGesture>(params);
-#endif
-
-  // Send pinch gesture and verify we receive the ack.
-  InputEventAckWaiter ack_waiter(host,
-                                 blink::WebInputEvent::Type::kGesturePinchEnd);
-  host->QueueSyntheticGesture(
-      std::move(synthetic_pinch_gesture),
-      base::BindOnce([](SyntheticGesture::Result result) {
-        EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
-      }));
-  ack_waiter.Wait();
-
-  // Make sure all the page scale values behave as expected.
-  const float kScaleTolerance = 0.1f;
-  observer_a.WaitForPageScaleFactor(target_page_scale, kScaleTolerance);
-  observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
-  observer_c.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
-  observer_d.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
-
-  // The change in |is_pinch_gesture_active| that signals the end of the pinch
-  // gesture will occur sometime after the ack for GesturePinchEnd, so we need
-  // to wait for it from each renderer. If it's never seen, the test fails by
-  // timing out.
-  interceptor_mainframe->WaitForPinchGestureEnd();
-  EXPECT_TRUE(interceptor_mainframe->pinch_gesture_active_set());
-  EXPECT_TRUE(interceptor_mainframe->pinch_gesture_active_cleared());
-  interceptor_child_b->WaitForPinchGestureEnd();
-  EXPECT_TRUE(interceptor_child_b->pinch_gesture_active_set());
-  EXPECT_TRUE(interceptor_child_b->pinch_gesture_active_cleared());
-}
-
 // Verify that sandbox flags specified by a CSP header are properly inherited by
 // child frames, but are removed when the frame navigates.
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
@@ -11570,143 +10392,6 @@
   EXPECT_EQ(0u, popup_process->GetFrameDepth());
 }
 
-// Flaky on multiple platforms (crbug.com/1094562).
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       DISABLED_FrameViewportIntersectionTestSimple) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b(c),d,e(f))"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  RenderFrameProxyHost* child2_proxy =
-      root->child_at(2)->render_manager()->GetProxyToParent();
-  auto child2_filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(child2_proxy);
-
-  // Force lifecycle update in root and child2 to make sure child2 has sent
-  // viewport intersection into to grand child before child2 becomes throttled.
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "")
-                  .error.empty());
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(
-                  root->child_at(2)->current_frame_host(), "", "")
-                  .error.empty());
-  child2_filter->Clear();
-
-  LayoutNonRecursiveForTestingViewportIntersection(shell()->web_contents());
-
-  // Root should always intersect.
-  EXPECT_TRUE(CheckIntersectsViewport(true, root));
-  // Child 0 should be entirely in viewport.
-  EXPECT_TRUE(CheckIntersectsViewport(true, root->child_at(0)));
-  // Make sure child0 has has a chance to propagate viewport intersection to
-  // grand child.
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(
-                  root->child_at(0)->current_frame_host(), "", "")
-                  .error.empty());
-  // Grand child should match parent.
-  EXPECT_TRUE(CheckIntersectsViewport(true, root->child_at(0)->child_at(0)));
-  // Child 1 should be partially in viewport.
-  EXPECT_TRUE(CheckIntersectsViewport(true, root->child_at(1)));
-  // Child 2 should be not be in viewport.
-  EXPECT_TRUE(CheckIntersectsViewport(false, root->child_at(2)));
-  // Can't use EvalJsAfterLifecycleUpdate on child2, because it's
-  // render-throttled. But it should still have propagated state down to the
-  // grandchild.
-  child2_filter->Wait();
-  // Grand child should match parent.
-  EXPECT_TRUE(CheckIntersectsViewport(false, root->child_at(2)->child_at(0)));
-}
-
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       FrameViewportOffsetTestSimple) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // This will catch b sending viewport intersection information to c.
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  RenderFrameProxyHost* iframe_c_proxy =
-      root->child_at(0)->child_at(0)->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(iframe_c_proxy);
-
-  // Use EvalJsAfterLifecycleUpdate to force animation frames in `a` and `b` to
-  // ensure that the viewport intersection for initial layout state has been
-  // propagated. The layout of `a` will not change again, so we can read back
-  // its layout info after the animation frame. The layout of `b` will change,
-  // so we don't read back its layout yet.
-  std::string script(R"(
-    let iframe = document.querySelector("iframe");
-    [iframe.offsetLeft, iframe.offsetTop];
-  )");
-  EvalJsResult iframe_b_result =
-      EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", script);
-  base::ListValue iframe_b_offset = iframe_b_result.ExtractList();
-  int iframe_b_offset_left = iframe_b_offset.GetList()[0].GetInt();
-  int iframe_b_offset_top = iframe_b_offset.GetList()[1].GetInt();
-
-  // Make sure a new IPC is sent after dirty-ing layout.
-  filter->Clear();
-
-  // Dirty layout in `b` to generate a new IPC to `c`. This will be the final
-  // layout state for `b`, so read back layout info here.
-  std::string raf_script(R"(
-    let iframe = document.querySelector("iframe");
-    let margin = getComputedStyle(iframe).marginTop.replace("px", "");
-    iframe.style.margin = String(parseInt(margin) + 1) + "px";
-  )");
-  EvalJsResult iframe_c_result = EvalJsAfterLifecycleUpdate(
-      root->child_at(0)->current_frame_host(), raf_script, script);
-  base::ListValue iframe_c_offset = iframe_c_result.ExtractList();
-  int iframe_c_offset_left = iframe_c_offset.GetList()[0].GetInt();
-  int iframe_c_offset_top = iframe_c_offset.GetList()[1].GetInt();
-
-  // The IPC should already have been sent
-  EXPECT_TRUE(filter->MessageReceived());
-
-  // +4 for a 2px border on each iframe.
-  gfx::PointF expected(iframe_b_offset_left + iframe_c_offset_left + 4,
-                       iframe_b_offset_top + iframe_c_offset_top + 4);
-  const float device_scale_factor =
-      root->render_manager()->GetRenderWidgetHostView()->GetDeviceScaleFactor();
-  // Convert from CSS to physical pixels
-  expected.Scale(device_scale_factor);
-  gfx::Transform actual = filter->GetIntersectionState()->main_frame_transform;
-  gfx::Point viewport_offset;
-  EXPECT_TRUE(actual.TransformPointReverse(&viewport_offset));
-  viewport_offset = gfx::Point(-viewport_offset.x(), -viewport_offset.y());
-  float tolerance = ceilf(device_scale_factor);
-  EXPECT_NEAR(expected.x(), viewport_offset.x(), tolerance);
-  EXPECT_NEAR(expected.y(), viewport_offset.y(), tolerance);
-}
-
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       FrameViewportIntersectionTestAggregate) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b,c,a,b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // Each immediate child is sized to 100% width and 75% height.
-  LayoutNonRecursiveForTestingViewportIntersection(shell()->web_contents());
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-
-  // Child 2 does not intersect, but shares widget with the main frame.
-  FrameTreeNode* node = root->child_at(2);
-  RenderProcessHost::Priority priority =
-      node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
-  EXPECT_TRUE(priority.intersects_viewport);
-  EXPECT_TRUE(
-      node->current_frame_host()->GetProcess()->GetIntersectsViewport());
-
-  // Child 3 does not intersect, but shares a process with child 0.
-  node = root->child_at(3);
-  priority = node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
-  EXPECT_FALSE(priority.intersects_viewport);
-  EXPECT_TRUE(
-      node->current_frame_host()->GetProcess()->GetIntersectsViewport());
-}
-
 // Check that when a postMessage is called on a remote frame, it waits for the
 // current script block to finish executing before forwarding the postMessage,
 // so that if the script causes any other IPCs to be sent in the same event
@@ -11798,487 +10483,6 @@
   EXPECT_EQ(start_url, rfh->GetLastCommittedURL());
 }
 
-// Tests that when a large OOPIF has been scaled, the compositor raster area
-// sent from the embedder is correct.
-#if defined(OS_ANDROID) || defined(OS_MAC)
-// Temporarily disabled on Android because this doesn't account for browser
-// control height or page scale factor.
-// Flaky on Mac. https://crbug.com/840314
-#define MAYBE_ScaledIframeRasterSize DISABLED_ScaledframeRasterSize
-#else
-#define MAYBE_ScaledIframeRasterSize ScaledIframeRasterSize
-#endif
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       MAYBE_ScaledIframeRasterSize) {
-  GURL http_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_scaled_large_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), http_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  FrameTreeNode* child = root->child_at(0);
-  RenderFrameProxyHost* child_proxy =
-      child->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
-
-  // Force a lifecycle update and wait for it to finish; by the time this call
-  // returns, the viewport intersection IPC should already have been received
-  // by the browser process and handled by the filter.
-  EvalJsResult eval_result = EvalJsAfterLifecycleUpdate(
-      root->current_frame_host(),
-      "document.getElementsByTagName('div')[0].scrollTo(0, 5000);",
-      "document.getElementsByTagName('div')[0].getBoundingClientRect().top;");
-  ASSERT_TRUE(eval_result.error.empty());
-  int div_offset_top = eval_result.ExtractInt();
-  gfx::Rect compositing_rect =
-      filter->GetIntersectionState()->compositor_visible_rect;
-
-  float device_scale_factor = 1.0f;
-  if (IsUseZoomForDSFEnabled())
-    device_scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
-
-  // The math below replicates the calculations in
-  // RemoteFrameView::GetCompositingRect(). That could be subject to tweaking,
-  // which would have to be reflected in these test expectations. Also, any
-  // changes to Blink that would affect the size of the frame rect or the
-  // visibile viewport would need to be accounted for.
-  // The multiplication by 5 accounts for the 0.2 scale factor in the test,
-  // which increases the area that has to be drawn in the OOPIF.
-  int view_height = root->current_frame_host()
-                        ->GetRenderWidgetHost()
-                        ->GetView()
-                        ->GetViewBounds()
-                        .height() *
-                    5 * device_scale_factor;
-
-  // The raster size is expanded by a factor of 1.3 to allow for some scrolling
-  // without requiring re-raster. The expanded area to be rasterized should be
-  // centered around the iframe's visible area within the parent document, hence
-  // the expansion in each direction (top, bottom, left, right) is
-  // (0.15 * viewport dimension).
-  int expansion = ceilf(view_height * 0.15f);
-  int expected_height = view_height + expansion * 2;
-
-  // 5000 = div scroll offset in scaled pixels
-  // 5 = scale factor from top-level document to iframe contents
-  // 2 = iframe border in scaled pixels
-  int expected_offset =
-      ((5000 - (div_offset_top * 5) - 2) * device_scale_factor) - expansion;
-
-  // Allow a small amount for rounding differences from applying page and
-  // device scale factors at different times.
-  float tolerance = ceilf(device_scale_factor);
-  EXPECT_NEAR(compositing_rect.height(), expected_height, tolerance);
-  EXPECT_NEAR(compositing_rect.y(), expected_offset, tolerance);
-}
-
-// Similar to ScaledIFrameRasterSize but with nested OOPIFs to ensure
-// propagation works correctly.
-#if defined(OS_ANDROID)
-// Temporarily disabled on Android because this doesn't account for browser
-// control height or page scale factor.
-#define MAYBE_ScaledNestedIframeRasterSize DISABLED_ScaledNestedIframeRasterSize
-#else
-#define MAYBE_ScaledNestedIframeRasterSize ScaledNestedIframeRasterSize
-#endif
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       MAYBE_ScaledNestedIframeRasterSize) {
-  GURL http_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_scaled_large_frames_nested.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), http_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-  FrameTreeNode* child_b = root->child_at(0);
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(
-      child_b,
-      embedded_test_server()->GetURL(
-          "bar.com", "/frame_tree/page_with_large_scrollable_frame.html")));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   +--Site B ------- proxies for A C\n"
-      "        +--Site C -- proxies for A B\n"
-      "Where A = http://a.com/\n"
-      "      B = http://bar.com/\n"
-      "      C = http://baz.com/",
-      DepictFrameTree(root));
-
-  // This adds the filter to the immediate child iframe. It verifies that the
-  // child sets the nested iframe's compositing rect correctly.
-  FrameTreeNode* child_c = child_b->child_at(0);
-  RenderFrameProxyHost* child_c_proxy =
-      child_c->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_c_proxy);
-
-  // Scroll the child frame so that it is partially clipped. This will cause the
-  // top 10 pixels of the child frame to be clipped. Applying the scale factor
-  // means that in the coordinate system of the subframes, 50px are clipped.
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(),
-                                         "window.scrollBy(0, 10)", "")
-                  .error.empty());
-
-  // This scrolls the div containing in the 'Site B' iframe that contains the
-  // 'Site C' iframe, and then we verify that the 'Site C' frame receives the
-  // correct compositor frame. Force a lifecycle update after the scroll and
-  // wait for it to finish; by the time this call returns, the viewport
-  // intersection IPC should already have been received by the browser process
-  // and handled by the filter. Extract the page offset of the leaf iframe
-  // within the middle document.
-  EvalJsResult child_eval_result = EvalJsAfterLifecycleUpdate(
-      child_b->current_frame_host(),
-      "document.getElementsByTagName('div')[0].scrollTo(0, 5000);",
-      "document.getElementsByTagName('div')[0].getBoundingClientRect().top;");
-  ASSERT_TRUE(child_eval_result.error.empty());
-  int child_div_offset_top = child_eval_result.ExtractInt();
-
-  gfx::Rect compositing_rect =
-      filter->GetIntersectionState()->compositor_visible_rect;
-
-  float scale_factor = 1.0f;
-  if (IsUseZoomForDSFEnabled())
-    scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
-
-  // See comment in ScaledIframeRasterSize for explanation of this. In this
-  // case, the raster area of the large iframe should be restricted to
-  // approximately the area of its containing frame which is unclipped by the
-  // main frame. The containing frame is clipped by 50 pixels at the top, due
-  // to the scroll offset of the main frame, so we subtract that from the full
-  // height of the containing frame.
-  int view_height = (child_b->current_frame_host()
-                         ->GetRenderWidgetHost()
-                         ->GetView()
-                         ->GetViewBounds()
-                         .height() -
-                     50) *
-                    scale_factor;
-  // 30% padding is added to the view_height to prevent frequent re-rasters.
-  // The extra padding is centered around the view height, hence expansion by
-  // 0.15 in each direction.
-  int expansion = ceilf(view_height * 0.15f);
-  int expected_height = view_height + expansion * 2;
-
-  // Explanation of terms:
-  //   5000 = offset from top of nested iframe to top of containing div, due to
-  //          scroll offset of div
-  //   child_div_offset_top = offset of containing div from top of child frame
-  //   50 = offset of child frame's intersection with the top document viewport
-  //       from the top of the child frame (i.e, clipped amount at top of child)
-  //   view_height * 0.15 = padding added to the top of the compositing rect
-  //                        (half the the 30% total padding)
-  int expected_offset =
-      5000 - ((child_div_offset_top - 50) * scale_factor) - expansion;
-
-  // Allow a small amount for rounding differences from applying page and
-  // device scale factors at different times.
-  EXPECT_NEAR(compositing_rect.height(), expected_height, ceilf(scale_factor));
-  EXPECT_NEAR(compositing_rect.y(), expected_offset, ceilf(scale_factor));
-}
-
-// Tests that when an OOPIF is inside a multicolumn container, its compositing
-// rect is set correctly.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       IframeInMulticolCompositingRect) {
-  GURL http_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_iframe_in_multicol.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), http_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  FrameTreeNode* child = root->child_at(0);
-  RenderFrameProxyHost* child_proxy =
-      child->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
-
-  // Force a lifecycle update and wait for it to finish. Changing the width of
-  // the iframe should cause the parent renderer to propagate a new
-  // ViewportIntersectionState while running the rendering pipeline. By the time
-  // this call returns, the viewport intersection IPC should already have been
-  // received by the browser process and handled by the filter.
-  EvalJsResult eval_result = EvalJsAfterLifecycleUpdate(
-      root->current_frame_host(),
-      "document.querySelector('iframe').style.width = '250px'", "");
-  ASSERT_TRUE(filter->MessageReceived());
-  gfx::Rect compositing_rect =
-      filter->GetIntersectionState()->compositor_visible_rect;
-
-  float scale_factor = 1.0f;
-  if (IsUseZoomForDSFEnabled())
-    scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
-
-  gfx::Point visible_offset(0, 0);
-  gfx::Size visible_size =
-      gfx::ScaleToFlooredSize(gfx::Size(250, 150), scale_factor, scale_factor);
-  gfx::Rect visible_rect(visible_offset, visible_size);
-  float tolerance = ceilf(scale_factor);
-  EXPECT_NEAR(compositing_rect.x(), visible_rect.x(), tolerance);
-  EXPECT_NEAR(compositing_rect.y(), visible_rect.y(), tolerance);
-  EXPECT_NEAR(compositing_rect.width(), visible_rect.width(), tolerance);
-  EXPECT_NEAR(compositing_rect.height(), visible_rect.height(), tolerance);
-  EXPECT_TRUE(compositing_rect.Contains(visible_rect));
-}
-
-// TODO(crbug.com/1168036): Flaky test.
-IN_PROC_BROWSER_TEST_P(
-    SitePerProcessBrowserTest,
-    DISABLED_NestedIframeTransformedIntoViewViewportIntersection) {
-  GURL http_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_frame_transformed_into_viewport.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), http_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-  FrameTreeNode* child_b = root->child_at(0);
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(
-      child_b,
-      embedded_test_server()->GetURL(
-          "bar.com", "/frame_tree/page_with_cross_origin_frame_at_half.html")));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   +--Site B ------- proxies for A C\n"
-      "        +--Site C -- proxies for A B\n"
-      "Where A = http://a.com/\n"
-      "      B = http://bar.com/\n"
-      "      C = http://baz.com/",
-      DepictFrameTree(root));
-
-  FrameTreeNode* child_c = child_b->child_at(0);
-  RenderFrameProxyHost* child_c_proxy =
-      child_c->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_c_proxy);
-
-  // Scroll the div containing the 'Site B' iframe to trigger a viewport
-  // intersection update.
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(
-                  child_b->current_frame_host(),
-                  "document.getElementsByTagName('div')[0].scrollTo(0, 5000);",
-                  "")
-                  .error.empty());
-  ASSERT_TRUE(filter->MessageReceived());
-
-  // Check that we currently intersect with the viewport.
-  gfx::Rect viewport_intersection =
-      filter->GetIntersectionState()->viewport_intersection;
-
-  EXPECT_GT(viewport_intersection.height(), 0);
-  EXPECT_GT(viewport_intersection.width(), 0);
-}
-
-// Test that the compositing scale factor for an out-of-process iframe are set
-// and updated correctly, including accounting for all intermediate transforms.
-// TODO(crbug.com/1164391): Flaky test.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       DISABLED_CompositingScaleFactorInNestedFrameTest) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_scaled_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* child_b = root->child_at(0);
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(
-      child_b, embedded_test_server()->GetURL(
-                   "b.com", "/frame_tree/page_with_transformed_iframe.html")));
-
-  ASSERT_EQ(1U, child_b->child_count());
-  FrameTreeNode* child_c = child_b->child_at(0);
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(
-      child_c, embedded_test_server()->GetURL(
-                   "c.com", "/frame_tree/page_with_scaled_frame.html")));
-
-  ASSERT_EQ(1U, child_c->child_count());
-  FrameTreeNode* child_d = child_c->child_at(0);
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(
-      child_d, embedded_test_server()->GetURL("d.com", "/simple_page.html")));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C D\n"
-      "   +--Site B ------- proxies for A C D\n"
-      "        +--Site C -- proxies for A B D\n"
-      "             +--Site D -- proxies for A B C\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/\n"
-      "      C = http://c.com/\n"
-      "      D = http://d.com/",
-      DepictFrameTree(root));
-
-  // Wait for b.com's frame to have its compositing scale factor set to 0.5,
-  // which is the scale factor for b.com's iframe element in the main frame.
-  while (true) {
-    auto* rwh_b = child_b->current_frame_host()->GetRenderWidgetHost();
-    absl::optional<blink::VisualProperties> properties =
-        rwh_b->LastComputedVisualProperties();
-    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
-                          properties->compositing_scale_factor, 0.5f)) {
-      break;
-    }
-    base::RunLoop().RunUntilIdle();
-  }
-
-  // Wait for c.com's frame to have its compositing scale factor set to 0.5,
-  // which is the accumulated scale factor of c.com to the main frame obtained
-  // by multiplying the scale factor of c.com's iframe element (1 since
-  // transform is rotation only without scale) with the scale factor of its
-  // parent frame b.com (0.5).
-  while (true) {
-    auto* rwh_c = child_c->current_frame_host()->GetRenderWidgetHost();
-    absl::optional<blink::VisualProperties> properties =
-        rwh_c->LastComputedVisualProperties();
-    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
-                          properties->compositing_scale_factor, 0.5f)) {
-      break;
-    }
-    base::RunLoop().RunUntilIdle();
-  }
-
-  // Wait for d.com's frame to have its compositing scale factor set to 0.25,
-  // which is the accumulated scale factor of d.com to the main frame obtained
-  // by combining the scale factor of d.com's iframe element (0.5) with the
-  // scale factor of its parent d.com (0.5).
-  while (true) {
-    auto* rwh_d = child_d->current_frame_host()->GetRenderWidgetHost();
-    absl::optional<blink::VisualProperties> properties =
-        rwh_d->LastComputedVisualProperties();
-    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
-                          properties->compositing_scale_factor, 0.25f)) {
-      break;
-    }
-    base::RunLoop().RunUntilIdle();
-  }
-}
-
-// Test that the compositing scale factor for an out-of-process iframe is set
-// to a non-zero value even if intermediate CSS transform has zero scale.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       CompositingScaleFactorWithZeroScaleTransform) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/page_with_scaled_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-
-  ASSERT_EQ(1U, root->child_count());
-  FrameTreeNode* child_b = root->child_at(0);
-
-  EXPECT_TRUE(NavigateToURLFromRenderer(
-      child_b,
-      embedded_test_server()->GetURL("b.com", "/frame_tree/simple_page.html")));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site B ------- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/",
-      DepictFrameTree(root));
-
-  // Wait for b.com's frame to have its compositing scale factor set to 0.5,
-  // which is the scale factor for b.com's iframe element in the main frame.
-  while (true) {
-    auto* rwh_b = child_b->current_frame_host()->GetRenderWidgetHost();
-    absl::optional<blink::VisualProperties> properties =
-        rwh_b->LastComputedVisualProperties();
-    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
-                          properties->compositing_scale_factor, 0.5f)) {
-      break;
-    }
-    base::RunLoop().RunUntilIdle();
-  }
-
-  // Set iframe transform scale to 0.
-  EXPECT_TRUE(
-      EvalJs(root->current_frame_host(),
-             "document.querySelector('iframe').style.transform = 'scale(0)'")
-          .error.empty());
-
-  // Wait for b.com frame's compositing scale factor to change, and check that
-  // the final value is non-zero.
-  while (true) {
-    auto* rwh_b = child_b->current_frame_host()->GetRenderWidgetHost();
-    absl::optional<blink::VisualProperties> properties =
-        rwh_b->LastComputedVisualProperties();
-    if (properties && !cc::MathUtil::IsFloatNearlyTheSame(
-                          properties->compositing_scale_factor, 0.5f)) {
-      EXPECT_GT(properties->compositing_scale_factor, 0.0f);
-      break;
-    }
-    base::RunLoop().RunUntilIdle();
-  }
-}
-
-// Verify that OOPIF select element popup menu coordinates account for scroll
-// offset in containers embedding frame.
-// TODO(crbug.com/859552): Reenable this.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       DISABLED_PopupMenuInTallIframeTest) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "/frame_tree/page_with_tall_positioned_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  FrameTreeNode* child_node = root->child_at(0);
-  GURL site_url(embedded_test_server()->GetURL(
-      "baz.com", "/site_isolation/page-with-select.html"));
-  EXPECT_TRUE(NavigateToURLFromRenderer(child_node, site_url));
-
-  RenderFrameProxyHost* root_proxy = root->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(root_proxy);
-
-  // Position the select element so that it is out of the viewport, then scroll
-  // it into view.
-  EXPECT_TRUE(ExecJs(child_node,
-                     "document.querySelector('select').style.top='2000px';"));
-  EXPECT_TRUE(ExecJs(root, "window.scrollTo(0, 1900);"));
-
-  // Wait for a viewport intersection update to be dispatched to the child, and
-  // ensure it is processed by the browser before continuing.
-  filter->Wait();
-  {
-    // This yields the UI thread in order to ensure that the new viewport
-    // intersection is sent to the to child renderer before the mouse click
-    // below.
-    base::RunLoop loop;
-    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
-                                                  loop.QuitClosure());
-    loop.Run();
-  }
-
-  auto show_popup_waiter = std::make_unique<ShowPopupWidgetWaiter>(
-      web_contents(), child_node->current_frame_host());
-  SimulateMouseClick(child_node->current_frame_host()->GetRenderWidgetHost(),
-                     55, 2005);
-
-  // Dismiss the popup.
-  SimulateMouseClick(child_node->current_frame_host()->GetRenderWidgetHost(), 1,
-                     1);
-
-  // The test passes if this wait returns, indicating that the popup was
-  // scrolled into view and the OOPIF renderer displayed it. Other tests verify
-  // the correctness of popup menu coordinates.
-  show_popup_waiter->Wait();
-}
-
 #if defined(OS_ANDROID)
 
 // This test ensures that gestures from child frames notify the gesture manager
@@ -12381,110 +10585,6 @@
 }
 #endif  // if defined(OS_ANDROID)
 
-// Test to verify that viewport intersection is propagated to nested OOPIFs
-// even when a parent OOPIF has been throttled.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       NestedFrameViewportIntersectionUpdated) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "foo.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  FrameTreeNode* child_node = root->child_at(0);
-  GURL site_url(embedded_test_server()->GetURL(
-      "bar.com", "/frame_tree/page_with_positioned_frame.html"));
-  EXPECT_TRUE(NavigateToURLFromRenderer(child_node, site_url));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   +--Site B ------- proxies for A C\n"
-      "        +--Site C -- proxies for A B\n"
-      "Where A = http://foo.com/\n"
-      "      B = http://bar.com/\n"
-      "      C = http://baz.com/",
-      DepictFrameTree(root));
-
-  // This will intercept messages sent from B to C, describing C's viewport
-  // intersection.
-  RenderFrameProxyHost* child_proxy =
-      child_node->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
-
-  // Run requestAnimationFrame in A and B to make sure initial layout has
-  // completed and initial IPCs sent.
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "")
-                  .error.empty());
-  ASSERT_TRUE(
-      EvalJsAfterLifecycleUpdate(child_node->current_frame_host(), "", "")
-          .error.empty());
-  filter->Clear();
-
-  // Scroll the child frame out of view, causing it to become throttled.
-  ASSERT_TRUE(ExecJs(root->current_frame_host(), "window.scrollTo(0, 5000)"));
-  filter->Wait();
-  EXPECT_TRUE(filter->GetIntersectionState()->viewport_intersection.IsEmpty());
-
-  // Scroll the frame back into view.
-  ASSERT_TRUE(ExecJs(root->current_frame_host(), "window.scrollTo(0, 0)"));
-  filter->Wait();
-  EXPECT_FALSE(filter->GetIntersectionState()->viewport_intersection.IsEmpty());
-}
-
-// Test to verify that the main frame document intersection
-// is propagated to out of process iframes by scrolling a nested iframe
-// in and out of intersecting with the main frame document.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       NestedFrameMainFrameDocumentIntersectionUpdated) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "foo.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  FrameTreeNode* child_node_b = root->child_at(0);
-  GURL site_url(embedded_test_server()->GetURL(
-      "bar.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
-  EXPECT_TRUE(NavigateToURLFromRenderer(child_node_b, site_url));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B C\n"
-      "   +--Site B ------- proxies for A C\n"
-      "        +--Site C -- proxies for A B\n"
-      "Where A = http://foo.com/\n"
-      "      B = http://bar.com/\n"
-      "      C = http://baz.com/",
-      DepictFrameTree(root));
-
-  FrameTreeNode* child_node_c = child_node_b->child_at(0);
-  RenderFrameProxyHost* child_proxy_c =
-      child_node_c->render_manager()->GetProxyToParent();
-  auto filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy_c);
-
-  // Run requestAnimationFrame in A and B to make sure initial layout has
-  // completed and initial IPC's sent.
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "")
-                  .error.empty());
-  ASSERT_TRUE(
-      EvalJsAfterLifecycleUpdate(child_node_b->current_frame_host(), "", "")
-          .error.empty());
-  filter->Clear();
-
-  // Scroll the child frame out of view, causing it to become throttled.
-  ASSERT_TRUE(
-      ExecJs(child_node_b->current_frame_host(), "window.scrollTo(0, 5000)"));
-  filter->Wait();
-  EXPECT_TRUE(
-      filter->GetIntersectionState()->main_frame_intersection.IsEmpty());
-
-  // Scroll the frame back into view.
-  ASSERT_TRUE(
-      ExecJs(child_node_b->current_frame_host(), "window.scrollTo(0, 0)"));
-  filter->Wait();
-  EXPECT_FALSE(
-      filter->GetIntersectionState()->main_frame_intersection.IsEmpty());
-}
-
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, DisplayLockThrottlesOOPIF) {
   GURL url_a(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
@@ -13155,48 +11255,6 @@
       1);
 }
 
-// Check that when a frame changes a subframe's size twice and then sends a
-// postMessage to the subframe, the subframe's onmessage handler sees the new
-// size.  In particular, ensure that the postMessage won't get reordered with
-// the second resize, which might be throttled if the first resize is still in
-// progress. See https://crbug.com/828529.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       ResizeAndCrossProcessPostMessagePreserveOrder) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-
-  // Add an onmessage handler to the subframe to send back its width.
-  EXPECT_TRUE(ExecJs(root->child_at(0), R"(
-      window.addEventListener('message', function(event) {
-        domAutomationController.send(document.body.clientWidth);
-      });)"));
-
-  // Drop the visual properties ACKs from the child renderer.  To do this,
-  // unsubscribe the child's RenderWidgetHost from its
-  // RenderFrameMetadataProvider, which ensures that
-  // DidUpdateVisualProperties() won't be called on it, and the ACK won't be
-  // reset.  This simulates that the ACK for the first resize below does not
-  // arrive before the second resize IPC arrives from the
-  // parent, and that the second resize IPC early-exits in
-  // SynchronizeVisualProperties() due to the pending visual properties ACK.
-  RenderWidgetHostImpl* rwh =
-      root->child_at(0)->current_frame_host()->GetRenderWidgetHost();
-  rwh->render_frame_metadata_provider_.RemoveObserver(rwh);
-
-  // Now, resize the subframe twice from the main frame and send it a
-  // postMessage. The postMessage handler should see the second updated size.
-  EXPECT_EQ(700, EvalJs(root, R"(
-      var f = document.querySelector('iframe');
-      f.width = 500;
-      f.offsetTop; // force layout; this sends a resize IPC for width of 500.
-      f.width = 700;
-      f.offsetTop; // force layout; this sends a resize IPC for width of 700.
-      f.contentWindow.postMessage('foo', '*');)",
-                        EXECUTE_SCRIPT_USE_MANUAL_REPLY));
-}
-
 class SitePerProcessAndProcessPerSiteBrowserTest
     : public SitePerProcessBrowserTest {
  public:
@@ -14253,160 +12311,6 @@
   EXPECT_FALSE(did_start_navigation_observer.observed());
 }
 
-// This test verifies that when scrolling an OOPIF in a pinched-zoomed page,
-// that the scroll-delta matches the distance between TouchStart/End as seen
-// by the oopif, i.e. the oopif content 'sticks' to the finger during scrolling.
-// The relation is not exact, but should be close.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       ScrollOopifInPinchZoomedPage) {
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b)"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1u, root->child_count());
-  FrameTreeNode* child = root->child_at(0);
-  ASSERT_TRUE(child);
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site B ------- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/",
-      DepictFrameTree(root));
-
-  // Make B scrollable. The call to document.write will erase the html inside
-  // the OOPIF, leaving just a vertical column of 'Hello's.
-  std::string script =
-      "var s = '<div>Hello</div>\\n';\n"
-      "document.write(s.repeat(200));";
-  EXPECT_TRUE(ExecJs(child, script));
-
-  RenderFrameSubmissionObserver observer_a(root);
-  RenderFrameSubmissionObserver observer_b(child);
-
-  // We need to observe a root frame submission to pick up the initial page
-  // scale factor.
-  observer_a.WaitForAnyFrameSubmission();
-
-  const float kPageScaleDelta = 2.f;
-  // On desktop systems we expect |current_page_scale| to be 1.f, but on
-  // Android it will typically be less than 1.f, and may take on arbitrary
-  // values.
-  float original_page_scale =
-      observer_a.LastRenderFrameMetadata().page_scale_factor;
-  float target_page_scale = original_page_scale * kPageScaleDelta;
-
-  SyntheticPinchGestureParams params;
-  auto* host = static_cast<RenderWidgetHostImpl*>(
-      root->current_frame_host()->GetRenderWidgetHost());
-  RenderWidgetHostViewBase* root_view = host->GetView();
-  RenderWidgetHostViewBase* child_view =
-      static_cast<RenderWidgetHostImpl*>(
-          child->current_frame_host()->GetRenderWidgetHost())
-          ->GetView();
-  gfx::Rect bounds(root_view->GetViewBounds().size());
-  // The synthetic gesture code expects a location in root-view coordinates.
-  params.anchor = gfx::PointF(bounds.CenterPoint().x(), 70.f);
-  // In SyntheticPinchGestureParams, |scale_factor| is really a delta.
-  params.scale_factor = kPageScaleDelta;
-#if defined(OS_MAC)
-  auto synthetic_pinch_gesture =
-      std::make_unique<SyntheticTouchpadPinchGesture>(params);
-#else
-  auto synthetic_pinch_gesture =
-      std::make_unique<SyntheticTouchscreenPinchGesture>(params);
-#endif
-
-  // Send pinch gesture and verify we receive the ack.
-  {
-    InputEventAckWaiter ack_waiter(
-        host, blink::WebInputEvent::Type::kGesturePinchEnd);
-    host->QueueSyntheticGesture(
-        std::move(synthetic_pinch_gesture),
-        base::BindOnce([](SyntheticGesture::Result result) {
-          EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
-        }));
-    ack_waiter.Wait();
-  }
-
-  // Make sure all the page scale values behave as expected.
-  const float kScaleTolerance = 0.07f;
-  observer_a.WaitForPageScaleFactor(target_page_scale, kScaleTolerance);
-  observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
-  float final_page_scale =
-      observer_a.LastRenderFrameMetadata().page_scale_factor;
-
-  // Verify scroll position of OOPIF.
-  float initial_child_scroll = EvalJs(child, "window.scrollY").ExtractDouble();
-
-  // Send touch-initiated gesture scroll sequence to OOPIF.
-  // TODO(wjmaclean): GetViewBounds() is broken for OOPIFs when PSF != 1.f, so
-  // we calculate it manually. This will need to be update when GetViewBounds()
-  // in RenderWidgetHostViewBase is fixed. See https://crbug.com/928825.
-  auto child_bounds = child_view->GetViewBounds();
-  gfx::PointF child_upper_left =
-      child_view->TransformPointToRootCoordSpaceF(gfx::PointF(0, 0));
-  gfx::PointF child_lower_right = child_view->TransformPointToRootCoordSpaceF(
-      gfx::PointF(child_bounds.width(), child_bounds.height()));
-  gfx::PointF scroll_start_location_in_screen =
-      gfx::PointF((child_upper_left.x() + child_lower_right.x()) / 2.f,
-                  child_lower_right.y() - 10);
-  const float kScrollDelta = 100.f;
-  gfx::PointF scroll_end_location_in_screen =
-      scroll_start_location_in_screen + gfx::Vector2dF(0, -kScrollDelta);
-
-  // Create touch move sequence with discrete touch moves. Include a brief
-  // pause at the end to avoid the scroll flinging.
-  std::string actions_template = R"HTML(
-      [{
-        "source" : "touch",
-        "actions" : [
-          { "name": "pointerDown", "x": %f, "y": %f},
-          { "name": "pointerMove", "x": %f, "y": %f},
-          { "name": "pause", "duration": 300 },
-          { "name": "pointerUp"}
-        ]
-      }]
-  )HTML";
-  std::string touch_move_sequence_json = base::StringPrintf(
-      actions_template.c_str(), scroll_start_location_in_screen.x(),
-      scroll_start_location_in_screen.y(), scroll_end_location_in_screen.x(),
-      scroll_end_location_in_screen.y());
-  base::JSONReader::ValueWithError parsed_json =
-      base::JSONReader::ReadAndReturnValueWithError(touch_move_sequence_json);
-  ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
-  ActionsParser actions_parser(std::move(*parsed_json.value));
-
-  ASSERT_TRUE(actions_parser.Parse());
-  auto synthetic_scroll_gesture =
-      SyntheticGesture::Create(actions_parser.gesture_params());
-
-  {
-    auto* child_host = static_cast<RenderWidgetHostImpl*>(
-        child->current_frame_host()->GetRenderWidgetHost());
-    InputEventAckWaiter ack_waiter(
-        child_host, blink::WebInputEvent::Type::kGestureScrollEnd);
-    host->QueueSyntheticGesture(
-        std::move(synthetic_scroll_gesture),
-        base::BindOnce([](SyntheticGesture::Result result) {
-          EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
-        }));
-    ack_waiter.Wait();
-  }
-
-  // Verify new scroll position of OOPIF, should match touch sequence delta.
-  float expected_scroll_delta = kScrollDelta / final_page_scale;
-  float actual_scroll_delta =
-      EvalJs(child, "window.scrollY").ExtractDouble() - initial_child_scroll;
-
-  const float kScrollTolerance = 0.2f;
-  EXPECT_GT((1.f + kScrollTolerance) * expected_scroll_delta,
-            actual_scroll_delta);
-  EXPECT_LT((1.f - kScrollTolerance) * expected_scroll_delta,
-            actual_scroll_delta);
-}
-
 // Check that if a frame starts a navigation, and the frame's current process
 // dies before the response for the navigation comes back, the response will
 // not trigger a process kill and will be allowed to commit in a new process.
@@ -14646,134 +12550,6 @@
   }
 }
 
-// Tests that main_frame_scroll_offset is not shared by frames in the same
-// process. This is a regression test for https://crbug.com/1063760.
-//
-// Set up the frame tree to be A(B1(C1),B2(C2)). Send IPC's with different
-// ViewportIntersection information to B1 and B2, and then check that the
-// information they propagate to C1 and C2 is different.
-// Disabled because of https://crbug.com/1136263
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
-                       DISABLED_MainFrameScrollOffset) {
-  GURL a_url = embedded_test_server()->GetURL(
-      "a.com", "/frame_tree/scrollable_page_with_two_frames.html");
-  GURL b_url = embedded_test_server()->GetURL(
-      "b.com", "/frame_tree/page_with_large_iframe.html");
-  GURL c_url = embedded_test_server()->GetURL("c.com", "/title1.html");
-
-  EXPECT_TRUE(NavigateToURL(shell(), a_url));
-  FrameTreeNode* a_node = web_contents()->GetPrimaryFrameTree().root();
-
-  FrameTreeNode* b1_node = a_node->child_at(0);
-  EXPECT_TRUE(NavigateToURLFromRenderer(b1_node, b_url));
-
-  FrameTreeNode* c1_node = b1_node->child_at(0);
-  EXPECT_TRUE(NavigateToURLFromRenderer(c1_node, c_url));
-
-  FrameTreeNode* b2_node = a_node->child_at(1);
-  EXPECT_TRUE(NavigateToURLFromRenderer(b2_node, b_url));
-
-  FrameTreeNode* c2_node = b2_node->child_at(0);
-  EXPECT_TRUE(NavigateToURLFromRenderer(c2_node, c_url));
-
-  // This will intercept messages sent from B1 to C1, describing C1's viewport
-  // intersection.
-  RenderFrameProxyHost* c1_proxy =
-      c1_node->render_manager()->GetProxyToParent();
-  auto b1_to_c1_message_filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(c1_proxy);
-
-  // This will intercept messages sent from B2 to C2, describing C2's viewport
-  // intersection.
-  RenderFrameProxyHost* c2_proxy =
-      c2_node->render_manager()->GetProxyToParent();
-  auto b2_to_c2_message_filter =
-      std::make_unique<UpdateViewportIntersectionMessageFilter>(c2_proxy);
-
-  // Running requestAnimationFrame will ensure that any pending IPC's have been
-  // sent by the renderer and received by the browser.
-  auto flush_ipcs = [](FrameTreeNode* node) {
-    ASSERT_TRUE(EvalJsAfterLifecycleUpdate(node->current_frame_host(), "", "")
-                    .error.empty());
-  };
-
-  flush_ipcs(a_node);
-  flush_ipcs(b1_node);
-  flush_ipcs(b2_node);
-  b1_to_c1_message_filter->Clear();
-  b2_to_c2_message_filter->Clear();
-
-  // Now that everything is in a stable, consistent state, we will send viewport
-  // intersection IPC's to B1 and B2 that contain a different
-  // main_frame_scroll_offset, and then verify that each of them propagates
-  // their own value of main_frame_scroll_offset to C1 and C2, respectively.
-  // The IPC code mimics messages that A would send to B1 and B2.
-  auto b1_intersection_state = b1_node->render_manager()
-                                   ->GetProxyToParent()
-                                   ->cross_process_frame_connector()
-                                   ->intersection_state();
-
-  b1_intersection_state.main_frame_scroll_offset.Offset(10, 0);
-  // A change in main_frame_scroll_offset by itself will not cause B1 to be
-  // marked dirty, so we also modify viewport_intersection.
-  b1_intersection_state.viewport_intersection.set_y(
-      b1_intersection_state.viewport_intersection.y() + 7);
-  b1_intersection_state.viewport_intersection.set_height(
-      b1_intersection_state.viewport_intersection.height() - 7);
-
-  ForceUpdateViewportIntersection(b1_node, b1_intersection_state);
-
-  auto b2_intersection_state = b2_node->render_manager()
-                                   ->GetProxyToParent()
-                                   ->cross_process_frame_connector()
-                                   ->intersection_state();
-
-  b2_intersection_state.main_frame_scroll_offset.Offset(20, 0);
-  b2_intersection_state.viewport_intersection.set_y(
-      b2_intersection_state.viewport_intersection.y() + 7);
-  b2_intersection_state.viewport_intersection.set_height(
-      b2_intersection_state.viewport_intersection.height() - 7);
-
-  ForceUpdateViewportIntersection(b2_node, b2_intersection_state);
-
-  // Once IPC's have been flushed to the C frames, we should see conflicting
-  // values for main_frame_scroll_offset.
-  flush_ipcs(b1_node);
-  flush_ipcs(b2_node);
-  ASSERT_TRUE(b1_to_c1_message_filter->MessageReceived());
-  ASSERT_TRUE(b2_to_c2_message_filter->MessageReceived());
-  EXPECT_EQ(
-      b1_to_c1_message_filter->GetIntersectionState()->main_frame_scroll_offset,
-      gfx::Point(10, 0));
-  EXPECT_EQ(
-      b2_to_c2_message_filter->GetIntersectionState()->main_frame_scroll_offset,
-      gfx::Point(20, 0));
-  b1_to_c1_message_filter->Clear();
-  b2_to_c2_message_filter->Clear();
-
-  // If we scroll the main frame, it should propagate IPC's which re-synchronize
-  // the values for all child frames.
-  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(a_node->current_frame_host(),
-                                         "window.scrollTo(0, 5)", "")
-                  .error.empty());
-  flush_ipcs(b1_node);
-  flush_ipcs(b2_node);
-  ASSERT_TRUE(b1_to_c1_message_filter->MessageReceived());
-  ASSERT_TRUE(b2_to_c2_message_filter->MessageReceived());
-
-  // Window scroll offset will be scaled by device scale factor
-  const float device_scale_factor = a_node->render_manager()
-                                        ->GetRenderWidgetHostView()
-                                        ->GetDeviceScaleFactor();
-  float expected_y = device_scale_factor * 5.0;
-  EXPECT_NEAR(b1_to_c1_message_filter->GetIntersectionState()
-                  ->main_frame_scroll_offset.y(),
-              expected_y, 1.f);
-  EXPECT_NEAR(b2_to_c2_message_filter->GetIntersectionState()
-                  ->main_frame_scroll_offset.y(),
-              expected_y, 1.f);
-}
-
 IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
                        AccessWindowProxyOfCrashedFrameAfterNavigation) {
   EXPECT_TRUE(NavigateToURL(
@@ -14837,89 +12613,6 @@
   destroyed_watcher.Wait();
 }
 
-class SitePerProcessCompositorViewportBrowserTest
-    : public SitePerProcessBrowserTestBase,
-      public testing::WithParamInterface<double> {
- public:
-  SitePerProcessCompositorViewportBrowserTest() {}
-
- protected:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor,
-                                    base::StringPrintf("%f", GetParam()));
-  }
-};
-
-// DISABLED: crbug.com/1071995
-IN_PROC_BROWSER_TEST_P(SitePerProcessCompositorViewportBrowserTest,
-                       DISABLED_OopifCompositorViewportSizeRelativeToParent) {
-  // Load page with very tall OOPIF.
-  GURL main_url(
-      embedded_test_server()->GetURL("a.com", "/super_tall_parent.html"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
-                            ->GetPrimaryFrameTree()
-                            .root();
-  ASSERT_EQ(1U, root->child_count());
-
-  FrameTreeNode* child = root->child_at(0);
-
-  GURL nested_site_url(
-      embedded_test_server()->GetURL("b.com", "/super_tall_page.html"));
-  EXPECT_TRUE(NavigateToURLFromRenderer(child, nested_site_url));
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site B ------- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/",
-      DepictFrameTree(root));
-
-  // Observe frame submission from parent.
-  RenderFrameSubmissionObserver parent_observer(
-      root->current_frame_host()
-          ->GetRenderWidgetHost()
-          ->render_frame_metadata_provider());
-  parent_observer.WaitForAnyFrameSubmission();
-  gfx::Size parent_viewport_size =
-      parent_observer.LastRenderFrameMetadata().viewport_size_in_pixels;
-
-  // Observe frame submission from child.
-  RenderFrameSubmissionObserver child_observer(
-      child->current_frame_host()
-          ->GetRenderWidgetHost()
-          ->render_frame_metadata_provider());
-  child_observer.WaitForAnyFrameSubmission();
-  gfx::Size child_viewport_size =
-      child_observer.LastRenderFrameMetadata().viewport_size_in_pixels;
-
-  // Verify child's compositor viewport is no more than about 30% larger than
-  // the parent's. See RemoteFrameView::GetCompositingRect() for explanation of
-  // the choice of 30%. Add +1 to child viewport height to account for rounding.
-  EXPECT_GE(ceilf(1.3f * parent_viewport_size.height()),
-            child_viewport_size.height() - 1);
-
-  // Verify the child's ViewBounds are much larger.
-  RenderWidgetHostViewBase* child_rwhv = static_cast<RenderWidgetHostViewBase*>(
-      child->current_frame_host()->GetRenderWidgetHost()->GetView());
-  // 30,000 is based on div/iframe sizes in the test HTML files.
-  EXPECT_LT(30000, child_rwhv->GetViewBounds().height());
-}
-
-#if defined(OS_ANDROID)
-// Android doesn't support forcing device scale factor in tests.
-INSTANTIATE_TEST_SUITE_P(SitePerProcess,
-                         SitePerProcessCompositorViewportBrowserTest,
-                         testing::Values(1.0));
-#else
-INSTANTIATE_TEST_SUITE_P(SitePerProcess,
-                         SitePerProcessCompositorViewportBrowserTest,
-                         testing::Values(1.0, 1.5, 2.0));
-#endif
-
 // Check that initial navigations to renderer debug URLs mark the renderer
 // process as used, so that future navigations to sites that require a
 // dedicated process do not reuse that process.
@@ -14945,97 +12638,6 @@
   EXPECT_NE(js_process, web_contents()->GetMainFrame()->GetProcess());
 }
 
-// Tests that when a non-root frame in an iframe, performs a RAF to emulate a
-// scroll, that metrics are reported.
-IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ScrollByRAF) {
-  base::HistogramTester histogram_tester;
-  GURL main_url(embedded_test_server()->GetURL(
-      "a.com", "/cross_site_iframe_factory.html?a(b(b))"));
-  EXPECT_TRUE(NavigateToURL(shell(), main_url));
-
-  // It is safe to obtain the root frame tree node here, as it doesn't change.
-  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
-  ASSERT_EQ(1U, root->child_count());
-
-  EXPECT_EQ(
-      " Site A ------------ proxies for B\n"
-      "   +--Site B ------- proxies for A\n"
-      "        +--Site B -- proxies for A\n"
-      "Where A = http://a.com/\n"
-      "      B = http://b.com/",
-      DepictFrameTree(root));
-
-  // Layout all three frames, so that the animation has a region to mark dirty.
-  LayoutNonRecursiveForTestingViewportIntersection(root->current_frame_host());
-  LayoutNonRecursiveForTestingViewportIntersection(
-      root->child_at(0)->current_frame_host());
-  LayoutNonRecursiveForTestingViewportIntersection(
-      root->child_at(0)->child_at(0)->current_frame_host());
-
-  // Add a div to the nested iframe, so that it can be animated.
-  RenderFrameSubmissionObserver frame_observer(root->child_at(0)->child_at(0));
-  std::string addContent(R"(
-      var d = document.createElement('div');
-      d.id = 'animationtarget';
-      d.innerHTML = 'Hey Listen!';
-      document.body.appendChild(d);
-    )");
-  ASSERT_TRUE(
-      EvalJsAfterLifecycleUpdate(
-          root->child_at(0)->child_at(0)->current_frame_host(), "", addContent)
-          .error.empty());
-  frame_observer.WaitForAnyFrameSubmission();
-
-  // Fetch the initial metrics, as adding a div can incidentally trigger RAF
-  // metrics.
-  FetchHistogramsFromChildProcesses();
-  auto initial_samples = histogram_tester.GetAllSamples(
-      "Graphics.Smoothness.PercentDroppedFrames.MainThread.RAF");
-  ASSERT_EQ(initial_samples.size(), 0u);
-
-  const int pre_scroll_frame_count = frame_observer.render_frame_count();
-
-  // Run a RAF that takes more than one frame, as metrics due to not track
-  // frames where WillBeginMainFrame occurs before it is triggered. Subsequent
-  // RAFs in the sequence will be measured.
-  std::string scrollByRAF(R"(
-     var offset = 0;
-      function run() {
-        let child = document.getElementById("animationtarget");
-        var rect = child.getBoundingClientRect();
-        child.style = 'transform: translateY(' + parseInt(offset)+'px);';
-        offset += 1;
-        requestAnimationFrame(run);
-      }
-      run();
-     )");
-  ASSERT_TRUE(
-      EvalJsAfterLifecycleUpdate(
-          root->child_at(0)->child_at(0)->current_frame_host(), scrollByRAF, "")
-          .error.empty());
-
-  // There will have been one frame before the RAF sequence. The minimum for
-  // reporting if 100 frames, however we need to wait at least one extra frame.
-  // On Android the animation begins during the initial call to
-  // EvalJsAfterLifecycleUpdate. However on Linux the first translate is not
-  // applied until the subsequent frame. So we wait for the minimum, then verify
-  // afterwards.
-  const int kExpectedNumberFrames = 101 + pre_scroll_frame_count;
-  while (frame_observer.render_frame_count() < kExpectedNumberFrames)
-    frame_observer.WaitForAnyFrameSubmission();
-
-  // We now wait for FrameSequenceTracker to time out in order for it to report.
-  // This will occur once the minimum 100 frames have been produced, and 5s have
-  // passed. If the test times out then the bug is back.
-  while (histogram_tester
-             .GetAllSamples(
-                 "Graphics.Smoothness.PercentDroppedFrames.MainThread.RAF")
-             .empty()) {
-    frame_observer.WaitForAnyFrameSubmission();
-    FetchHistogramsFromChildProcesses();
-  }
-}
-
 // Tests that verify the feature disabling process reuse.
 class DisableProcessReusePolicyTest : public SitePerProcessBrowserTest {
  public:
@@ -15111,9 +12713,6 @@
                          SitePerProcessBrowserTouchActionTest,
                          testing::ValuesIn(RenderDocumentFeatureLevelValues()));
 INSTANTIATE_TEST_SUITE_P(All,
-                         SitePerProcessHighDPIBrowserTest,
-                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
-INSTANTIATE_TEST_SUITE_P(All,
                          SitePerProcessIgnoreCertErrorsBrowserTest,
                          testing::ValuesIn(RenderDocumentFeatureLevelValues()));
 INSTANTIATE_TEST_SUITE_P(All,
diff --git a/content/browser/site_per_process_layout_browsertest.cc b/content/browser/site_per_process_layout_browsertest.cc
new file mode 100644
index 0000000..d420ad48
--- /dev/null
+++ b/content/browser/site_per_process_layout_browsertest.cc
@@ -0,0 +1,2442 @@
+// 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/site_per_process_browsertest.h"
+
+#include "base/json/json_reader.h"
+#include "build/build_config.h"
+#include "content/browser/renderer_host/cross_process_frame_connector.h"
+#include "content/browser/renderer_host/input/synthetic_touchscreen_pinch_gesture.h"
+#include "content/browser/renderer_host/render_widget_host_view_child_frame.h"
+#include "content/common/input/actions_parser.h"
+#include "content/public/common/content_switches.h"
+#include "content/public/common/use_zoom_for_dsf_policy.h"
+#include "content/public/test/browser_test.h"
+#include "content/public/test/browser_test_utils.h"
+#include "content/public/test/content_browser_test_utils.h"
+#include "content/public/test/hit_test_region_observer.h"
+#include "content/public/test/test_frame_navigation_observer.h"
+#include "content/test/render_document_feature.h"
+#include "content/test/render_widget_host_visibility_observer.h"
+
+#if defined(USE_AURA)
+#include "ui/aura/window_tree_host.h"
+#endif
+
+#if defined(OS_MAC)
+#include "content/browser/renderer_host/input/synthetic_touchpad_pinch_gesture.h"
+#include "ui/base/test/scoped_preferred_scroller_style_mac.h"
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+#include "ui/aura/test/test_screen.h"
+#endif
+
+namespace content {
+
+namespace {
+
+double GetFrameDeviceScaleFactor(const ToRenderFrameHost& adapter) {
+  return EvalJs(adapter, "window.devicePixelRatio;").ExtractDouble();
+}
+
+// Layout child frames in cross_site_iframe_factory.html so that they are the
+// same width as the viewport, and 75% of the height of the window. This is for
+// testing viewport intersection. Note this does not recurse into child frames
+// and re-layout in the same way since children might be in a different origin.
+void LayoutNonRecursiveForTestingViewportIntersection(
+    const ToRenderFrameHost& execution_target) {
+  static const char kRafScript[] = R"(
+      let width = window.innerWidth;
+      let height = window.innerHeight * 0.75;
+      for (let i = 0; i < window.frames.length; i++) {
+        let child = document.getElementById("child-" + i);
+        child.width = width;
+        child.height = height;
+      }
+  )";
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(execution_target, kRafScript, "")
+                  .error.empty());
+}
+
+// Check |intersects_viewport| on widget and process.
+bool CheckIntersectsViewport(bool expected, FrameTreeNode* node) {
+  RenderProcessHost::Priority priority =
+      node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
+  return priority.intersects_viewport == expected &&
+         node->current_frame_host()->GetProcess()->GetIntersectsViewport() ==
+             expected;
+}
+
+// Helper function to generate a click on the given RenderWidgetHost.  The
+// mouse event is forwarded directly to the RenderWidgetHost without any
+// hit-testing.
+void SimulateMouseClick(RenderWidgetHost* rwh, int x, int y) {
+  blink::WebMouseEvent mouse_event(
+      blink::WebInputEvent::Type::kMouseDown,
+      blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+  mouse_event.button = blink::WebPointerProperties::Button::kLeft;
+  mouse_event.SetPositionInWidget(x, y);
+  rwh->ForwardMouseEvent(mouse_event);
+}
+
+}  // namespace
+
+// Class to monitor incoming UpdateViewportIntersection messages.
+class UpdateViewportIntersectionMessageFilter
+    : public blink::mojom::RemoteFrameHostInterceptorForTesting {
+ public:
+  explicit UpdateViewportIntersectionMessageFilter(
+      content::RenderFrameProxyHost* rfph)
+      : intersection_state_(blink::mojom::ViewportIntersectionState::New()),
+        render_frame_proxy_host_(rfph) {
+    render_frame_proxy_host_->frame_host_receiver_for_testing()
+        .SwapImplForTesting(this);
+  }
+
+  const blink::mojom::ViewportIntersectionStatePtr& GetIntersectionState()
+      const {
+    return intersection_state_;
+  }
+
+  RenderFrameProxyHost* GetForwardingInterface() override {
+    return render_frame_proxy_host_;
+  }
+
+  void UpdateViewportIntersection(
+      blink::mojom::ViewportIntersectionStatePtr intersection_state,
+      const absl::optional<blink::FrameVisualProperties>& visual_properties)
+      override {
+    intersection_state_ = std::move(intersection_state);
+    msg_received_ = true;
+    if (run_loop_)
+      run_loop_->Quit();
+  }
+
+  bool MessageReceived() const { return msg_received_; }
+
+  void Clear() {
+    msg_received_ = false;
+    intersection_state_ = blink::mojom::ViewportIntersectionState::New();
+  }
+
+  void Wait() {
+    DCHECK(!run_loop_);
+    if (msg_received_) {
+      msg_received_ = false;
+      return;
+    }
+    std::unique_ptr<base::RunLoop> run_loop(new base::RunLoop);
+    run_loop_ = run_loop.get();
+    run_loop_->Run();
+    run_loop_ = nullptr;
+    msg_received_ = false;
+  }
+
+  void set_run_loop(base::RunLoop* run_loop) { run_loop_ = run_loop; }
+
+ private:
+  raw_ptr<base::RunLoop> run_loop_ = nullptr;
+  bool msg_received_;
+  blink::mojom::ViewportIntersectionStatePtr intersection_state_;
+  raw_ptr<content::RenderFrameProxyHost> render_frame_proxy_host_;
+};
+
+// TODO(tonikitoo): Move to fake_remote_frame.h|cc in case it is useful
+// for other tests.
+class FakeRemoteMainFrame : public blink::mojom::RemoteMainFrame {
+ public:
+  FakeRemoteMainFrame() = default;
+  ~FakeRemoteMainFrame() override = default;
+
+  void Init(
+      mojo::PendingAssociatedReceiver<blink::mojom::RemoteMainFrame> receiver) {
+    receiver_.Bind(std::move(receiver));
+  }
+
+  // blink::mojom::RemoteMainFrame overrides:
+  void UpdateTextAutosizerPageInfo(
+      blink::mojom::TextAutosizerPageInfoPtr page_info) override {}
+
+ private:
+  mojo::AssociatedReceiver<blink::mojom::RemoteMainFrame> receiver_{this};
+};
+
+// This class intercepts RenderFrameProxyHost creations, and overrides their
+// respective blink::mojom::RemoteMainFrame instances, so that it can watch for
+// text autosizer page info updates.
+class UpdateTextAutosizerInfoProxyObserver
+    : public RenderFrameProxyHost::TestObserver {
+ public:
+  UpdateTextAutosizerInfoProxyObserver() {
+    RenderFrameProxyHost::SetObserverForTesting(this);
+  }
+  ~UpdateTextAutosizerInfoProxyObserver() override {
+    RenderFrameProxyHost::SetObserverForTesting(nullptr);
+  }
+
+  const blink::mojom::TextAutosizerPageInfo& TextAutosizerPageInfo(
+      RenderFrameProxyHost* proxy) {
+    return remote_frames_[proxy]->page_info();
+  }
+
+ private:
+  class Remote : public FakeRemoteMainFrame {
+   public:
+    explicit Remote(RenderFrameProxyHost* proxy) {
+      Init(proxy->BindRemoteMainFrameReceiverForTesting());
+    }
+    void UpdateTextAutosizerPageInfo(
+        blink::mojom::TextAutosizerPageInfoPtr page_info) override {
+      page_info_ = *page_info;
+    }
+    const blink::mojom::TextAutosizerPageInfo& page_info() {
+      return page_info_;
+    }
+
+   private:
+    blink::mojom::TextAutosizerPageInfo page_info_;
+  };
+
+  void OnRemoteMainFrameBound(RenderFrameProxyHost* proxy_host) override {
+    remote_frames_[proxy_host] = std::make_unique<Remote>(proxy_host);
+  }
+
+  std::map<RenderFrameProxyHost*, std::unique_ptr<Remote>> remote_frames_;
+};
+
+class TextAutosizerPageInfoInterceptor
+    : public blink::mojom::LocalMainFrameHostInterceptorForTesting {
+ public:
+  explicit TextAutosizerPageInfoInterceptor(
+      RenderFrameHostImpl* render_frame_host)
+      : render_frame_host_(render_frame_host) {
+    render_frame_host_->local_main_frame_host_receiver_for_testing()
+        .SwapImplForTesting(this);
+  }
+
+  ~TextAutosizerPageInfoInterceptor() override = default;
+
+  LocalMainFrameHost* GetForwardingInterface() override {
+    return render_frame_host_;
+  }
+
+  void WaitForPageInfo(absl::optional<int> target_main_frame_width,
+                       absl::optional<float> target_device_scale_adjustment) {
+    if (remote_page_info_seen_)
+      return;
+    target_main_frame_width_ = target_main_frame_width;
+    target_device_scale_adjustment_ = target_device_scale_adjustment;
+    run_loop_ = std::make_unique<base::RunLoop>();
+    run_loop_->Run();
+    run_loop_.reset();
+  }
+
+  const blink::mojom::TextAutosizerPageInfo& GetTextAutosizerPageInfo() {
+    return *remote_page_info_;
+  }
+
+  void TextAutosizerPageInfoChanged(
+      blink::mojom::TextAutosizerPageInfoPtr remote_page_info) override {
+    if ((!target_main_frame_width_ ||
+         remote_page_info->main_frame_width != target_main_frame_width_) &&
+        (!target_device_scale_adjustment_ ||
+         remote_page_info->device_scale_adjustment !=
+             target_device_scale_adjustment_)) {
+      return;
+    }
+    remote_page_info_ = remote_page_info.Clone();
+    remote_page_info_seen_ = true;
+    if (run_loop_)
+      run_loop_->Quit();
+    GetForwardingInterface()->TextAutosizerPageInfoChanged(
+        std::move(remote_page_info));
+  }
+
+ private:
+  raw_ptr<RenderFrameHostImpl> render_frame_host_;
+  bool remote_page_info_seen_ = false;
+  blink::mojom::TextAutosizerPageInfoPtr remote_page_info_ =
+      blink::mojom::TextAutosizerPageInfo::New(/*main_frame_width=*/0,
+                                               /*main_frame_layout_width=*/0,
+                                               /*device_scale_adjustment=*/1.f);
+  std::unique_ptr<base::RunLoop> run_loop_;
+  absl::optional<int> target_main_frame_width_;
+  absl::optional<float> target_device_scale_adjustment_;
+};
+
+class SitePerProcessHighDPIBrowserTest : public SitePerProcessBrowserTest {
+ public:
+  const double kDeviceScaleFactor = 2.0;
+
+  SitePerProcessHighDPIBrowserTest() = default;
+
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII(
+        switches::kForceDeviceScaleFactor,
+        base::StringPrintf("%f", kDeviceScaleFactor));
+  }
+};
+
+IN_PROC_BROWSER_TEST_P(SitePerProcessHighDPIBrowserTest,
+                       SubframeLoadsWithCorrectDeviceScaleFactor) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // On Android forcing device scale factor does not work for tests, therefore
+  // we ensure that make frame and iframe have the same DIP scale there, but
+  // not necessarily kDeviceScaleFactor.
+  const double expected_dip_scale =
+#if defined(OS_ANDROID)
+      GetFrameDeviceScaleFactor(web_contents());
+#else
+      SitePerProcessHighDPIBrowserTest::kDeviceScaleFactor;
+#endif
+
+  EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(web_contents()));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(root));
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* child = root->child_at(0);
+  EXPECT_EQ(expected_dip_scale, GetFrameDeviceScaleFactor(child));
+}
+
+class SitePerProcessCompositorViewportBrowserTest
+    : public SitePerProcessBrowserTestBase,
+      public testing::WithParamInterface<double> {
+ public:
+  SitePerProcessCompositorViewportBrowserTest() = default;
+
+ protected:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    SitePerProcessBrowserTestBase::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII(switches::kForceDeviceScaleFactor,
+                                    base::StringPrintf("%f", GetParam()));
+  }
+};
+
+// DISABLED: crbug.com/1071995
+IN_PROC_BROWSER_TEST_P(SitePerProcessCompositorViewportBrowserTest,
+                       DISABLED_OopifCompositorViewportSizeRelativeToParent) {
+  // Load page with very tall OOPIF.
+  GURL main_url(
+      embedded_test_server()->GetURL("a.com", "/super_tall_parent.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* child = root->child_at(0);
+
+  GURL nested_site_url(
+      embedded_test_server()->GetURL("b.com", "/super_tall_page.html"));
+  EXPECT_TRUE(NavigateToURLFromRenderer(child, nested_site_url));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  // Observe frame submission from parent.
+  RenderFrameSubmissionObserver parent_observer(
+      root->current_frame_host()
+          ->GetRenderWidgetHost()
+          ->render_frame_metadata_provider());
+  parent_observer.WaitForAnyFrameSubmission();
+  gfx::Size parent_viewport_size =
+      parent_observer.LastRenderFrameMetadata().viewport_size_in_pixels;
+
+  // Observe frame submission from child.
+  RenderFrameSubmissionObserver child_observer(
+      child->current_frame_host()
+          ->GetRenderWidgetHost()
+          ->render_frame_metadata_provider());
+  child_observer.WaitForAnyFrameSubmission();
+  gfx::Size child_viewport_size =
+      child_observer.LastRenderFrameMetadata().viewport_size_in_pixels;
+
+  // Verify child's compositor viewport is no more than about 30% larger than
+  // the parent's. See RemoteFrameView::GetCompositingRect() for explanation of
+  // the choice of 30%. Add +1 to child viewport height to account for rounding.
+  EXPECT_GE(ceilf(1.3f * parent_viewport_size.height()),
+            child_viewport_size.height() - 1);
+
+  // Verify the child's ViewBounds are much larger.
+  RenderWidgetHostViewBase* child_rwhv = static_cast<RenderWidgetHostViewBase*>(
+      child->current_frame_host()->GetRenderWidgetHost()->GetView());
+  // 30,000 is based on div/iframe sizes in the test HTML files.
+  EXPECT_LT(30000, child_rwhv->GetViewBounds().height());
+}
+
+#if defined(OS_ANDROID)
+// Android doesn't support forcing device scale factor in tests.
+INSTANTIATE_TEST_SUITE_P(SitePerProcess,
+                         SitePerProcessCompositorViewportBrowserTest,
+                         testing::Values(1.0));
+#else
+INSTANTIATE_TEST_SUITE_P(SitePerProcess,
+                         SitePerProcessCompositorViewportBrowserTest,
+                         testing::Values(1.0, 1.5, 2.0));
+#endif
+
+#if BUILDFLAG(IS_CHROMEOS_ASH)
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       SubframeUpdateToCorrectDeviceScaleFactor) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  EXPECT_EQ(1.0, GetFrameDeviceScaleFactor(web_contents()));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* child = root->child_at(0);
+  EXPECT_EQ(1.0, GetFrameDeviceScaleFactor(child));
+
+  double expected_dip_scale = 2.0;
+
+  // TODO(oshima): allow DeviceScaleFactor change on other platforms
+  // (win, linux, mac, android and mus).
+  aura::TestScreen* test_screen =
+      static_cast<aura::TestScreen*>(display::Screen::GetScreen());
+  test_screen->CreateHostForPrimaryDisplay();
+  test_screen->SetDeviceScaleFactor(expected_dip_scale);
+
+  // This forces |expected_dip_scale| to be applied to the aura::WindowTreeHost
+  // and aura::Window.
+  aura::WindowTreeHost* window_tree_host = shell()->window()->GetHost();
+  window_tree_host->SetBoundsInPixels(window_tree_host->GetBoundsInPixels());
+
+  // Wait until dppx becomes 2 if the frame's dpr hasn't beeen updated
+  // to 2 yet.
+  const char kScript[] =
+      "function sendDpr() "
+      "{window.domAutomationController.send(window.devicePixelRatio);}; "
+      "if (window.devicePixelRatio == 2) sendDpr();"
+      "window.matchMedia('screen and "
+      "(min-resolution: 2dppx)').addListener(function(e) { if (e.matches) { "
+      "sendDpr();}})";
+  // Make sure that both main frame and iframe are updated to 2x.
+  EXPECT_EQ(expected_dip_scale,
+            EvalJs(child, kScript, content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
+                .ExtractDouble());
+
+  EXPECT_EQ(expected_dip_scale, EvalJs(web_contents(), kScript,
+                                       content::EXECUTE_SCRIPT_USE_MANUAL_REPLY)
+                                    .ExtractDouble());
+}
+
+#endif
+
+// Tests that when a large OOPIF has been scaled, the compositor raster area
+// sent from the embedder is correct.
+#if defined(OS_ANDROID) || defined(OS_MAC)
+// Temporarily disabled on Android because this doesn't account for browser
+// control height or page scale factor.
+// Flaky on Mac. https://crbug.com/840314
+#define MAYBE_ScaledIframeRasterSize DISABLED_ScaledframeRasterSize
+#else
+#define MAYBE_ScaledIframeRasterSize ScaledIframeRasterSize
+#endif
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       MAYBE_ScaledIframeRasterSize) {
+  GURL http_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_scaled_large_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), http_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+
+  FrameTreeNode* child = root->child_at(0);
+  RenderFrameProxyHost* child_proxy =
+      child->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
+
+  // Force a lifecycle update and wait for it to finish; by the time this call
+  // returns, the viewport intersection IPC should already have been received
+  // by the browser process and handled by the filter.
+  EvalJsResult eval_result = EvalJsAfterLifecycleUpdate(
+      root->current_frame_host(),
+      "document.getElementsByTagName('div')[0].scrollTo(0, 5000);",
+      "document.getElementsByTagName('div')[0].getBoundingClientRect().top;");
+  ASSERT_TRUE(eval_result.error.empty());
+  int div_offset_top = eval_result.ExtractInt();
+  gfx::Rect compositing_rect =
+      filter->GetIntersectionState()->compositor_visible_rect;
+
+  float device_scale_factor = 1.0f;
+  if (IsUseZoomForDSFEnabled())
+    device_scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
+
+  // The math below replicates the calculations in
+  // RemoteFrameView::GetCompositingRect(). That could be subject to tweaking,
+  // which would have to be reflected in these test expectations. Also, any
+  // changes to Blink that would affect the size of the frame rect or the
+  // visible viewport would need to be accounted for.
+  // The multiplication by 5 accounts for the 0.2 scale factor in the test,
+  // which increases the area that has to be drawn in the OOPIF.
+  int view_height = root->current_frame_host()
+                        ->GetRenderWidgetHost()
+                        ->GetView()
+                        ->GetViewBounds()
+                        .height() *
+                    5 * device_scale_factor;
+
+  // The raster size is expanded by a factor of 1.3 to allow for some scrolling
+  // without requiring re-raster. The expanded area to be rasterized should be
+  // centered around the iframe's visible area within the parent document, hence
+  // the expansion in each direction (top, bottom, left, right) is
+  // (0.15 * viewport dimension).
+  int expansion = ceilf(view_height * 0.15f);
+  int expected_height = view_height + expansion * 2;
+
+  // 5000 = div scroll offset in scaled pixels
+  // 5 = scale factor from top-level document to iframe contents
+  // 2 = iframe border in scaled pixels
+  int expected_offset =
+      ((5000 - (div_offset_top * 5) - 2) * device_scale_factor) - expansion;
+
+  // Allow a small amount for rounding differences from applying page and
+  // device scale factors at different times.
+  float tolerance = ceilf(device_scale_factor);
+  EXPECT_NEAR(compositing_rect.height(), expected_height, tolerance);
+  EXPECT_NEAR(compositing_rect.y(), expected_offset, tolerance);
+}
+
+// Similar to ScaledIFrameRasterSize but with nested OOPIFs to ensure
+// propagation works correctly.
+#if defined(OS_ANDROID)
+// Temporarily disabled on Android because this doesn't account for browser
+// control height or page scale factor.
+#define MAYBE_ScaledNestedIframeRasterSize DISABLED_ScaledNestedIframeRasterSize
+#else
+#define MAYBE_ScaledNestedIframeRasterSize ScaledNestedIframeRasterSize
+#endif
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       MAYBE_ScaledNestedIframeRasterSize) {
+  GURL http_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_scaled_large_frames_nested.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), http_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+  FrameTreeNode* child_b = root->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_b,
+      embedded_test_server()->GetURL(
+          "bar.com", "/frame_tree/page_with_large_scrollable_frame.html")));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   +--Site B ------- proxies for A C\n"
+      "        +--Site C -- proxies for A B\n"
+      "Where A = http://a.com/\n"
+      "      B = http://bar.com/\n"
+      "      C = http://baz.com/",
+      DepictFrameTree(root));
+
+  // This adds the filter to the immediate child iframe. It verifies that the
+  // child sets the nested iframe's compositing rect correctly.
+  FrameTreeNode* child_c = child_b->child_at(0);
+  RenderFrameProxyHost* child_c_proxy =
+      child_c->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_c_proxy);
+
+  // Scroll the child frame so that it is partially clipped. This will cause the
+  // top 10 pixels of the child frame to be clipped. Applying the scale factor
+  // means that in the coordinate system of the subframes, 50px are clipped.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(),
+                                         "window.scrollBy(0, 10)", "")
+                  .error.empty());
+
+  // This scrolls the div containing in the 'Site B' iframe that contains the
+  // 'Site C' iframe, and then we verify that the 'Site C' frame receives the
+  // correct compositor frame. Force a lifecycle update after the scroll and
+  // wait for it to finish; by the time this call returns, the viewport
+  // intersection IPC should already have been received by the browser process
+  // and handled by the filter. Extract the page offset of the leaf iframe
+  // within the middle document.
+  EvalJsResult child_eval_result = EvalJsAfterLifecycleUpdate(
+      child_b->current_frame_host(),
+      "document.getElementsByTagName('div')[0].scrollTo(0, 5000);",
+      "document.getElementsByTagName('div')[0].getBoundingClientRect().top;");
+  ASSERT_TRUE(child_eval_result.error.empty());
+  int child_div_offset_top = child_eval_result.ExtractInt();
+
+  gfx::Rect compositing_rect =
+      filter->GetIntersectionState()->compositor_visible_rect;
+
+  float scale_factor = 1.0f;
+  if (IsUseZoomForDSFEnabled())
+    scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
+
+  // See comment in ScaledIframeRasterSize for explanation of this. In this
+  // case, the raster area of the large iframe should be restricted to
+  // approximately the area of its containing frame which is unclipped by the
+  // main frame. The containing frame is clipped by 50 pixels at the top, due
+  // to the scroll offset of the main frame, so we subtract that from the full
+  // height of the containing frame.
+  int view_height = (child_b->current_frame_host()
+                         ->GetRenderWidgetHost()
+                         ->GetView()
+                         ->GetViewBounds()
+                         .height() -
+                     50) *
+                    scale_factor;
+  // 30% padding is added to the view_height to prevent frequent re-rasters.
+  // The extra padding is centered around the view height, hence expansion by
+  // 0.15 in each direction.
+  int expansion = ceilf(view_height * 0.15f);
+  int expected_height = view_height + expansion * 2;
+
+  // Explanation of terms:
+  //   5000 = offset from top of nested iframe to top of containing div, due to
+  //          scroll offset of div
+  //   child_div_offset_top = offset of containing div from top of child frame
+  //   50 = offset of child frame's intersection with the top document viewport
+  //       from the top of the child frame (i.e, clipped amount at top of child)
+  //   view_height * 0.15 = padding added to the top of the compositing rect
+  //                        (half the the 30% total padding)
+  int expected_offset =
+      5000 - ((child_div_offset_top - 50) * scale_factor) - expansion;
+
+  // Allow a small amount for rounding differences from applying page and
+  // device scale factors at different times.
+  EXPECT_NEAR(compositing_rect.height(), expected_height, ceilf(scale_factor));
+  EXPECT_NEAR(compositing_rect.y(), expected_offset, ceilf(scale_factor));
+}
+
+// Tests that when an OOPIF is inside a multicolumn container, its compositing
+// rect is set correctly.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       IframeInMulticolCompositingRect) {
+  GURL http_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_iframe_in_multicol.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), http_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+
+  FrameTreeNode* child = root->child_at(0);
+  RenderFrameProxyHost* child_proxy =
+      child->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
+
+  // Force a lifecycle update and wait for it to finish. Changing the width of
+  // the iframe should cause the parent renderer to propagate a new
+  // ViewportIntersectionState while running the rendering pipeline. By the time
+  // this call returns, the viewport intersection IPC should already have been
+  // received by the browser process and handled by the filter.
+  EvalJsResult eval_result = EvalJsAfterLifecycleUpdate(
+      root->current_frame_host(),
+      "document.querySelector('iframe').style.width = '250px'", "");
+  ASSERT_TRUE(filter->MessageReceived());
+  gfx::Rect compositing_rect =
+      filter->GetIntersectionState()->compositor_visible_rect;
+
+  float scale_factor = 1.0f;
+  if (IsUseZoomForDSFEnabled())
+    scale_factor = GetFrameDeviceScaleFactor(shell()->web_contents());
+
+  gfx::Point visible_offset(0, 0);
+  gfx::Size visible_size =
+      gfx::ScaleToFlooredSize(gfx::Size(250, 150), scale_factor, scale_factor);
+  gfx::Rect visible_rect(visible_offset, visible_size);
+  float tolerance = ceilf(scale_factor);
+  EXPECT_NEAR(compositing_rect.x(), visible_rect.x(), tolerance);
+  EXPECT_NEAR(compositing_rect.y(), visible_rect.y(), tolerance);
+  EXPECT_NEAR(compositing_rect.width(), visible_rect.width(), tolerance);
+  EXPECT_NEAR(compositing_rect.height(), visible_rect.height(), tolerance);
+  EXPECT_TRUE(compositing_rect.Contains(visible_rect));
+}
+
+// Flaky on multiple platforms (crbug.com/1094562).
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       DISABLED_FrameViewportIntersectionTestSimple) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c),d,e(f))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  RenderFrameProxyHost* child2_proxy =
+      root->child_at(2)->render_manager()->GetProxyToParent();
+  auto child2_filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child2_proxy);
+
+  // Force lifecycle update in root and child2 to make sure child2 has sent
+  // viewport intersection into to grand child before child2 becomes throttled.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(
+                  root->child_at(2)->current_frame_host(), "", "")
+                  .error.empty());
+  child2_filter->Clear();
+
+  LayoutNonRecursiveForTestingViewportIntersection(shell()->web_contents());
+
+  // Root should always intersect.
+  EXPECT_TRUE(CheckIntersectsViewport(true, root));
+  // Child 0 should be entirely in viewport.
+  EXPECT_TRUE(CheckIntersectsViewport(true, root->child_at(0)));
+  // Make sure child0 has has a chance to propagate viewport intersection to
+  // grand child.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(
+                  root->child_at(0)->current_frame_host(), "", "")
+                  .error.empty());
+  // Grand child should match parent.
+  EXPECT_TRUE(CheckIntersectsViewport(true, root->child_at(0)->child_at(0)));
+  // Child 1 should be partially in viewport.
+  EXPECT_TRUE(CheckIntersectsViewport(true, root->child_at(1)));
+  // Child 2 should be not be in viewport.
+  EXPECT_TRUE(CheckIntersectsViewport(false, root->child_at(2)));
+  // Can't use EvalJsAfterLifecycleUpdate on child2, because it's
+  // render-throttled. But it should still have propagated state down to the
+  // grandchild.
+  child2_filter->Wait();
+  // Grand child should match parent.
+  EXPECT_TRUE(CheckIntersectsViewport(false, root->child_at(2)->child_at(0)));
+}
+
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       FrameViewportOffsetTestSimple) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // This will catch b sending viewport intersection information to c.
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  RenderFrameProxyHost* iframe_c_proxy =
+      root->child_at(0)->child_at(0)->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(iframe_c_proxy);
+
+  // Use EvalJsAfterLifecycleUpdate to force animation frames in `a` and `b` to
+  // ensure that the viewport intersection for initial layout state has been
+  // propagated. The layout of `a` will not change again, so we can read back
+  // its layout info after the animation frame. The layout of `b` will change,
+  // so we don't read back its layout yet.
+  std::string script(R"(
+    let iframe = document.querySelector("iframe");
+    [iframe.offsetLeft, iframe.offsetTop];
+  )");
+  EvalJsResult iframe_b_result =
+      EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", script);
+  base::ListValue iframe_b_offset = iframe_b_result.ExtractList();
+  int iframe_b_offset_left = iframe_b_offset.GetList()[0].GetInt();
+  int iframe_b_offset_top = iframe_b_offset.GetList()[1].GetInt();
+
+  // Make sure a new IPC is sent after dirty-ing layout.
+  filter->Clear();
+
+  // Dirty layout in `b` to generate a new IPC to `c`. This will be the final
+  // layout state for `b`, so read back layout info here.
+  std::string raf_script(R"(
+    let iframe = document.querySelector("iframe");
+    let margin = getComputedStyle(iframe).marginTop.replace("px", "");
+    iframe.style.margin = String(parseInt(margin) + 1) + "px";
+  )");
+  EvalJsResult iframe_c_result = EvalJsAfterLifecycleUpdate(
+      root->child_at(0)->current_frame_host(), raf_script, script);
+  base::ListValue iframe_c_offset = iframe_c_result.ExtractList();
+  int iframe_c_offset_left = iframe_c_offset.GetList()[0].GetInt();
+  int iframe_c_offset_top = iframe_c_offset.GetList()[1].GetInt();
+
+  // The IPC should already have been sent
+  EXPECT_TRUE(filter->MessageReceived());
+
+  // +4 for a 2px border on each iframe.
+  gfx::PointF expected(iframe_b_offset_left + iframe_c_offset_left + 4,
+                       iframe_b_offset_top + iframe_c_offset_top + 4);
+  const float device_scale_factor =
+      root->render_manager()->GetRenderWidgetHostView()->GetDeviceScaleFactor();
+  // Convert from CSS to physical pixels
+  expected.Scale(device_scale_factor);
+  gfx::Transform actual = filter->GetIntersectionState()->main_frame_transform;
+  gfx::Point viewport_offset;
+  EXPECT_TRUE(actual.TransformPointReverse(&viewport_offset));
+  viewport_offset = gfx::Point(-viewport_offset.x(), -viewport_offset.y());
+  float tolerance = ceilf(device_scale_factor);
+  EXPECT_NEAR(expected.x(), viewport_offset.x(), tolerance);
+  EXPECT_NEAR(expected.y(), viewport_offset.y(), tolerance);
+}
+
+// TODO(crbug.com/1168036): Flaky test.
+IN_PROC_BROWSER_TEST_P(
+    SitePerProcessBrowserTest,
+    DISABLED_NestedIframeTransformedIntoViewViewportIntersection) {
+  GURL http_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_frame_transformed_into_viewport.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), http_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+  FrameTreeNode* child_b = root->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_b,
+      embedded_test_server()->GetURL(
+          "bar.com", "/frame_tree/page_with_cross_origin_frame_at_half.html")));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   +--Site B ------- proxies for A C\n"
+      "        +--Site C -- proxies for A B\n"
+      "Where A = http://a.com/\n"
+      "      B = http://bar.com/\n"
+      "      C = http://baz.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* child_c = child_b->child_at(0);
+  RenderFrameProxyHost* child_c_proxy =
+      child_c->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_c_proxy);
+
+  // Scroll the div containing the 'Site B' iframe to trigger a viewport
+  // intersection update.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(
+                  child_b->current_frame_host(),
+                  "document.getElementsByTagName('div')[0].scrollTo(0, 5000);",
+                  "")
+                  .error.empty());
+  ASSERT_TRUE(filter->MessageReceived());
+
+  // Check that we currently intersect with the viewport.
+  gfx::Rect viewport_intersection =
+      filter->GetIntersectionState()->viewport_intersection;
+
+  EXPECT_GT(viewport_intersection.height(), 0);
+  EXPECT_GT(viewport_intersection.width(), 0);
+}
+
+// Verify that OOPIF select element popup menu coordinates account for scroll
+// offset in containers embedding frame.
+// TODO(crbug.com/859552): Reenable this.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       DISABLED_PopupMenuInTallIframeTest) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "/frame_tree/page_with_tall_positioned_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  FrameTreeNode* child_node = root->child_at(0);
+  GURL site_url(embedded_test_server()->GetURL(
+      "baz.com", "/site_isolation/page-with-select.html"));
+  EXPECT_TRUE(NavigateToURLFromRenderer(child_node, site_url));
+
+  RenderFrameProxyHost* root_proxy = root->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(root_proxy);
+
+  // Position the select element so that it is out of the viewport, then scroll
+  // it into view.
+  EXPECT_TRUE(ExecJs(child_node,
+                     "document.querySelector('select').style.top='2000px';"));
+  EXPECT_TRUE(ExecJs(root, "window.scrollTo(0, 1900);"));
+
+  // Wait for a viewport intersection update to be dispatched to the child, and
+  // ensure it is processed by the browser before continuing.
+  filter->Wait();
+  {
+    // This yields the UI thread in order to ensure that the new viewport
+    // intersection is sent to the to child renderer before the mouse click
+    // below.
+    base::RunLoop loop;
+    base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+                                                  loop.QuitClosure());
+    loop.Run();
+  }
+
+  auto show_popup_waiter = std::make_unique<ShowPopupWidgetWaiter>(
+      web_contents(), child_node->current_frame_host());
+  SimulateMouseClick(child_node->current_frame_host()->GetRenderWidgetHost(),
+                     55, 2005);
+
+  // Dismiss the popup.
+  SimulateMouseClick(child_node->current_frame_host()->GetRenderWidgetHost(), 1,
+                     1);
+
+  // The test passes if this wait returns, indicating that the popup was
+  // scrolled into view and the OOPIF renderer displayed it. Other tests verify
+  // the correctness of popup menu coordinates.
+  show_popup_waiter->Wait();
+}
+
+// Test to verify that viewport intersection is propagated to nested OOPIFs
+// even when a parent OOPIF has been throttled.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       NestedFrameViewportIntersectionUpdated) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "foo.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  FrameTreeNode* child_node = root->child_at(0);
+  GURL site_url(embedded_test_server()->GetURL(
+      "bar.com", "/frame_tree/page_with_positioned_frame.html"));
+  EXPECT_TRUE(NavigateToURLFromRenderer(child_node, site_url));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   +--Site B ------- proxies for A C\n"
+      "        +--Site C -- proxies for A B\n"
+      "Where A = http://foo.com/\n"
+      "      B = http://bar.com/\n"
+      "      C = http://baz.com/",
+      DepictFrameTree(root));
+
+  // This will intercept messages sent from B to C, describing C's viewport
+  // intersection.
+  RenderFrameProxyHost* child_proxy =
+      child_node->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy);
+
+  // Run requestAnimationFrame in A and B to make sure initial layout has
+  // completed and initial IPCs sent.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(
+      EvalJsAfterLifecycleUpdate(child_node->current_frame_host(), "", "")
+          .error.empty());
+  filter->Clear();
+
+  // Scroll the child frame out of view, causing it to become throttled.
+  ASSERT_TRUE(ExecJs(root->current_frame_host(), "window.scrollTo(0, 5000)"));
+  filter->Wait();
+  EXPECT_TRUE(filter->GetIntersectionState()->viewport_intersection.IsEmpty());
+
+  // Scroll the frame back into view.
+  ASSERT_TRUE(ExecJs(root->current_frame_host(), "window.scrollTo(0, 0)"));
+  filter->Wait();
+  EXPECT_FALSE(filter->GetIntersectionState()->viewport_intersection.IsEmpty());
+}
+
+// Test to verify that the main frame document intersection
+// is propagated to out of process iframes by scrolling a nested iframe
+// in and out of intersecting with the main frame document.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       NestedFrameMainFrameDocumentIntersectionUpdated) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "foo.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  FrameTreeNode* child_node_b = root->child_at(0);
+  GURL site_url(embedded_test_server()->GetURL(
+      "bar.com", "/frame_tree/scrollable_page_with_positioned_frame.html"));
+  EXPECT_TRUE(NavigateToURLFromRenderer(child_node_b, site_url));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C\n"
+      "   +--Site B ------- proxies for A C\n"
+      "        +--Site C -- proxies for A B\n"
+      "Where A = http://foo.com/\n"
+      "      B = http://bar.com/\n"
+      "      C = http://baz.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* child_node_c = child_node_b->child_at(0);
+  RenderFrameProxyHost* child_proxy_c =
+      child_node_c->render_manager()->GetProxyToParent();
+  auto filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(child_proxy_c);
+
+  // Run requestAnimationFrame in A and B to make sure initial layout has
+  // completed and initial IPC's sent.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(root->current_frame_host(), "", "")
+                  .error.empty());
+  ASSERT_TRUE(
+      EvalJsAfterLifecycleUpdate(child_node_b->current_frame_host(), "", "")
+          .error.empty());
+  filter->Clear();
+
+  // Scroll the child frame out of view, causing it to become throttled.
+  ASSERT_TRUE(
+      ExecJs(child_node_b->current_frame_host(), "window.scrollTo(0, 5000)"));
+  filter->Wait();
+  EXPECT_TRUE(
+      filter->GetIntersectionState()->main_frame_intersection.IsEmpty());
+
+  // Scroll the frame back into view.
+  ASSERT_TRUE(
+      ExecJs(child_node_b->current_frame_host(), "window.scrollTo(0, 0)"));
+  filter->Wait();
+  EXPECT_FALSE(
+      filter->GetIntersectionState()->main_frame_intersection.IsEmpty());
+}
+
+// Tests that main_frame_scroll_offset is not shared by frames in the same
+// process. This is a regression test for https://crbug.com/1063760.
+//
+// Set up the frame tree to be A(B1(C1),B2(C2)). Send IPC's with different
+// ViewportIntersection information to B1 and B2, and then check that the
+// information they propagate to C1 and C2 is different.
+// Disabled because of https://crbug.com/1136263
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       DISABLED_MainFrameScrollOffset) {
+  GURL a_url = embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/scrollable_page_with_two_frames.html");
+  GURL b_url = embedded_test_server()->GetURL(
+      "b.com", "/frame_tree/page_with_large_iframe.html");
+  GURL c_url = embedded_test_server()->GetURL("c.com", "/title1.html");
+
+  EXPECT_TRUE(NavigateToURL(shell(), a_url));
+  FrameTreeNode* a_node = web_contents()->GetPrimaryFrameTree().root();
+
+  FrameTreeNode* b1_node = a_node->child_at(0);
+  EXPECT_TRUE(NavigateToURLFromRenderer(b1_node, b_url));
+
+  FrameTreeNode* c1_node = b1_node->child_at(0);
+  EXPECT_TRUE(NavigateToURLFromRenderer(c1_node, c_url));
+
+  FrameTreeNode* b2_node = a_node->child_at(1);
+  EXPECT_TRUE(NavigateToURLFromRenderer(b2_node, b_url));
+
+  FrameTreeNode* c2_node = b2_node->child_at(0);
+  EXPECT_TRUE(NavigateToURLFromRenderer(c2_node, c_url));
+
+  // This will intercept messages sent from B1 to C1, describing C1's viewport
+  // intersection.
+  RenderFrameProxyHost* c1_proxy =
+      c1_node->render_manager()->GetProxyToParent();
+  auto b1_to_c1_message_filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(c1_proxy);
+
+  // This will intercept messages sent from B2 to C2, describing C2's viewport
+  // intersection.
+  RenderFrameProxyHost* c2_proxy =
+      c2_node->render_manager()->GetProxyToParent();
+  auto b2_to_c2_message_filter =
+      std::make_unique<UpdateViewportIntersectionMessageFilter>(c2_proxy);
+
+  // Running requestAnimationFrame will ensure that any pending IPC's have been
+  // sent by the renderer and received by the browser.
+  auto flush_ipcs = [](FrameTreeNode* node) {
+    ASSERT_TRUE(EvalJsAfterLifecycleUpdate(node->current_frame_host(), "", "")
+                    .error.empty());
+  };
+
+  flush_ipcs(a_node);
+  flush_ipcs(b1_node);
+  flush_ipcs(b2_node);
+  b1_to_c1_message_filter->Clear();
+  b2_to_c2_message_filter->Clear();
+
+  // Now that everything is in a stable, consistent state, we will send viewport
+  // intersection IPC's to B1 and B2 that contain a different
+  // main_frame_scroll_offset, and then verify that each of them propagates
+  // their own value of main_frame_scroll_offset to C1 and C2, respectively.
+  // The IPC code mimics messages that A would send to B1 and B2.
+  auto b1_intersection_state = b1_node->render_manager()
+                                   ->GetProxyToParent()
+                                   ->cross_process_frame_connector()
+                                   ->intersection_state();
+
+  b1_intersection_state.main_frame_scroll_offset.Offset(10, 0);
+  // A change in main_frame_scroll_offset by itself will not cause B1 to be
+  // marked dirty, so we also modify viewport_intersection.
+  b1_intersection_state.viewport_intersection.set_y(
+      b1_intersection_state.viewport_intersection.y() + 7);
+  b1_intersection_state.viewport_intersection.set_height(
+      b1_intersection_state.viewport_intersection.height() - 7);
+
+  ForceUpdateViewportIntersection(b1_node, b1_intersection_state);
+
+  auto b2_intersection_state = b2_node->render_manager()
+                                   ->GetProxyToParent()
+                                   ->cross_process_frame_connector()
+                                   ->intersection_state();
+
+  b2_intersection_state.main_frame_scroll_offset.Offset(20, 0);
+  b2_intersection_state.viewport_intersection.set_y(
+      b2_intersection_state.viewport_intersection.y() + 7);
+  b2_intersection_state.viewport_intersection.set_height(
+      b2_intersection_state.viewport_intersection.height() - 7);
+
+  ForceUpdateViewportIntersection(b2_node, b2_intersection_state);
+
+  // Once IPC's have been flushed to the C frames, we should see conflicting
+  // values for main_frame_scroll_offset.
+  flush_ipcs(b1_node);
+  flush_ipcs(b2_node);
+  ASSERT_TRUE(b1_to_c1_message_filter->MessageReceived());
+  ASSERT_TRUE(b2_to_c2_message_filter->MessageReceived());
+  EXPECT_EQ(
+      b1_to_c1_message_filter->GetIntersectionState()->main_frame_scroll_offset,
+      gfx::Point(10, 0));
+  EXPECT_EQ(
+      b2_to_c2_message_filter->GetIntersectionState()->main_frame_scroll_offset,
+      gfx::Point(20, 0));
+  b1_to_c1_message_filter->Clear();
+  b2_to_c2_message_filter->Clear();
+
+  // If we scroll the main frame, it should propagate IPC's which re-synchronize
+  // the values for all child frames.
+  ASSERT_TRUE(EvalJsAfterLifecycleUpdate(a_node->current_frame_host(),
+                                         "window.scrollTo(0, 5)", "")
+                  .error.empty());
+  flush_ipcs(b1_node);
+  flush_ipcs(b2_node);
+  ASSERT_TRUE(b1_to_c1_message_filter->MessageReceived());
+  ASSERT_TRUE(b2_to_c2_message_filter->MessageReceived());
+
+  // Window scroll offset will be scaled by device scale factor
+  const float device_scale_factor = a_node->render_manager()
+                                        ->GetRenderWidgetHostView()
+                                        ->GetDeviceScaleFactor();
+  float expected_y = device_scale_factor * 5.0;
+  EXPECT_NEAR(b1_to_c1_message_filter->GetIntersectionState()
+                  ->main_frame_scroll_offset.y(),
+              expected_y, 1.f);
+  EXPECT_NEAR(b2_to_c2_message_filter->GetIntersectionState()
+                  ->main_frame_scroll_offset.y(),
+              expected_y, 1.f);
+}
+
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       FrameViewportIntersectionTestAggregate) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b,c,a,b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // Each immediate child is sized to 100% width and 75% height.
+  LayoutNonRecursiveForTestingViewportIntersection(shell()->web_contents());
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+
+  // Child 2 does not intersect, but shares widget with the main frame.
+  FrameTreeNode* node = root->child_at(2);
+  RenderProcessHost::Priority priority =
+      node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
+  EXPECT_TRUE(priority.intersects_viewport);
+  EXPECT_TRUE(
+      node->current_frame_host()->GetProcess()->GetIntersectsViewport());
+
+  // Child 3 does not intersect, but shares a process with child 0.
+  node = root->child_at(3);
+  priority = node->current_frame_host()->GetRenderWidgetHost()->GetPriority();
+  EXPECT_FALSE(priority.intersects_viewport);
+  EXPECT_TRUE(
+      node->current_frame_host()->GetProcess()->GetIntersectsViewport());
+}
+
+// Tests that when a non-root frame in an iframe, performs a RAF to emulate a
+// scroll, that metrics are reported.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ScrollByRAF) {
+  base::HistogramTester histogram_tester;
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(b))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "        +--Site B -- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  // Layout all three frames, so that the animation has a region to mark dirty.
+  LayoutNonRecursiveForTestingViewportIntersection(root->current_frame_host());
+  LayoutNonRecursiveForTestingViewportIntersection(
+      root->child_at(0)->current_frame_host());
+  LayoutNonRecursiveForTestingViewportIntersection(
+      root->child_at(0)->child_at(0)->current_frame_host());
+
+  // Add a div to the nested iframe, so that it can be animated.
+  RenderFrameSubmissionObserver frame_observer(root->child_at(0)->child_at(0));
+  std::string addContent(R"(
+      var d = document.createElement('div');
+      d.id = 'animationtarget';
+      d.innerHTML = 'Hey Listen!';
+      document.body.appendChild(d);
+    )");
+  ASSERT_TRUE(
+      EvalJsAfterLifecycleUpdate(
+          root->child_at(0)->child_at(0)->current_frame_host(), "", addContent)
+          .error.empty());
+  frame_observer.WaitForAnyFrameSubmission();
+
+  // Fetch the initial metrics, as adding a div can incidentally trigger RAF
+  // metrics.
+  FetchHistogramsFromChildProcesses();
+  auto initial_samples = histogram_tester.GetAllSamples(
+      "Graphics.Smoothness.PercentDroppedFrames.MainThread.RAF");
+  ASSERT_EQ(initial_samples.size(), 0u);
+
+  const int pre_scroll_frame_count = frame_observer.render_frame_count();
+
+  // Run a RAF that takes more than one frame, as metrics due to not track
+  // frames where WillBeginMainFrame occurs before it is triggered. Subsequent
+  // RAFs in the sequence will be measured.
+  std::string scrollByRAF(R"(
+     var offset = 0;
+      function run() {
+        let child = document.getElementById("animationtarget");
+        var rect = child.getBoundingClientRect();
+        child.style = 'transform: translateY(' + parseInt(offset)+'px);';
+        offset += 1;
+        requestAnimationFrame(run);
+      }
+      run();
+     )");
+  ASSERT_TRUE(
+      EvalJsAfterLifecycleUpdate(
+          root->child_at(0)->child_at(0)->current_frame_host(), scrollByRAF, "")
+          .error.empty());
+
+  // There will have been one frame before the RAF sequence. The minimum for
+  // reporting if 100 frames, however we need to wait at least one extra frame.
+  // On Android the animation begins during the initial call to
+  // EvalJsAfterLifecycleUpdate. However on Linux the first translate is not
+  // applied until the subsequent frame. So we wait for the minimum, then verify
+  // afterwards.
+  const int kExpectedNumberFrames = 101 + pre_scroll_frame_count;
+  while (frame_observer.render_frame_count() < kExpectedNumberFrames)
+    frame_observer.WaitForAnyFrameSubmission();
+
+  // We now wait for FrameSequenceTracker to time out in order for it to report.
+  // This will occur once the minimum 100 frames have been produced, and 5s have
+  // passed. If the test times out then the bug is back.
+  while (histogram_tester
+             .GetAllSamples(
+                 "Graphics.Smoothness.PercentDroppedFrames.MainThread.RAF")
+             .empty()) {
+    frame_observer.WaitForAnyFrameSubmission();
+    FetchHistogramsFromChildProcesses();
+  }
+}
+
+// Make sure that when a relevant feature of the main frame changes, e.g. the
+// frame width, that the browser is notified.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, TextAutosizerPageInfo) {
+  UpdateTextAutosizerInfoProxyObserver update_text_autosizer_info_observer;
+
+  blink::web_pref::WebPreferences prefs =
+      web_contents()->GetOrCreateWebPreferences();
+  prefs.text_autosizing_enabled = true;
+
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  web_contents()->SetWebPreferences(prefs);
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* b_child = root->child_at(0);
+
+  blink::mojom::TextAutosizerPageInfo received_page_info;
+  auto interceptor = std::make_unique<TextAutosizerPageInfoInterceptor>(
+      web_contents()->GetMainFrame());
+#if defined(OS_ANDROID)
+  prefs.device_scale_adjustment += 0.05f;
+  // Change the device scale adjustment to trigger a RemotePageInfo update.
+  web_contents()->SetWebPreferences(prefs);
+  // Make sure we receive a ViewHostMsg from the main frame's renderer.
+  interceptor->WaitForPageInfo(absl::optional<int>(),
+                               prefs.device_scale_adjustment);
+  // Make sure the correct page message is sent to the child.
+  base::RunLoop().RunUntilIdle();
+  received_page_info = interceptor->GetTextAutosizerPageInfo();
+  EXPECT_EQ(prefs.device_scale_adjustment,
+            received_page_info.device_scale_adjustment);
+#else
+  // Resize the main frame, then wait to observe that the RemotePageInfo message
+  // arrives.
+  auto* view = web_contents()->GetRenderWidgetHostView();
+  gfx::Rect old_bounds = view->GetViewBounds();
+  gfx::Rect new_bounds(
+      old_bounds.origin(),
+      gfx::Size(old_bounds.width() - 20, old_bounds.height() - 20));
+
+  view->SetBounds(new_bounds);
+  // Make sure we receive a ViewHostMsg from the main frame's renderer.
+  interceptor->WaitForPageInfo(new_bounds.width(), absl::optional<float>());
+  // Make sure the correct page message is sent to the child.
+  base::RunLoop().RunUntilIdle();
+  received_page_info = interceptor->GetTextAutosizerPageInfo();
+  EXPECT_EQ(new_bounds.width(), received_page_info.main_frame_width);
+#endif  // defined(OS_ANDROID)
+
+  // Dynamically create a new, cross-process frame to test sending the cached
+  // TextAutosizerPageInfo.
+
+  GURL c_url = embedded_test_server()->GetURL("c.com", "/title1.html");
+  // The following is a hack so we can get an IPC watcher connected to the
+  // RenderProcessHost for C before the RenderView is created for it, and the
+  // TextAutosizerPageInfo IPC is sent to it.
+  scoped_refptr<SiteInstance> c_site =
+      web_contents()->GetSiteInstance()->GetRelatedSiteInstance(c_url);
+  // Force creation of a render process for c's SiteInstance, this will get
+  // used when we dynamically create the new frame.
+  auto* c_rph = static_cast<RenderProcessHostImpl*>(c_site->GetProcess());
+  ASSERT_TRUE(c_rph);
+  ASSERT_NE(c_rph, root->current_frame_host()->GetProcess());
+  ASSERT_NE(c_rph, b_child->current_frame_host()->GetProcess());
+
+  // Create the subframe now.
+  std::string create_frame_script = base::StringPrintf(
+      "var new_iframe = document.createElement('iframe');"
+      "new_iframe.src = '%s';"
+      "document.body.appendChild(new_iframe);",
+      c_url.spec().c_str());
+  EXPECT_TRUE(ExecJs(root, create_frame_script));
+  ASSERT_EQ(2U, root->child_count());
+
+  // Ensure IPC is sent.
+  base::RunLoop().RunUntilIdle();
+  blink::mojom::TextAutosizerPageInfo page_info_sent_to_remote_main_frames =
+      update_text_autosizer_info_observer.TextAutosizerPageInfo(
+          web_contents()
+              ->GetRenderManager()
+              ->GetAllProxyHostsForTesting()
+              .begin()
+              ->second.get());
+
+  EXPECT_EQ(received_page_info.main_frame_width,
+            page_info_sent_to_remote_main_frames.main_frame_width);
+  EXPECT_EQ(received_page_info.main_frame_layout_width,
+            page_info_sent_to_remote_main_frames.main_frame_layout_width);
+  EXPECT_EQ(received_page_info.device_scale_adjustment,
+            page_info_sent_to_remote_main_frames.device_scale_adjustment);
+}
+
+// Test that the physical backing size and view bounds for a scaled out-of-
+// process iframe are set and updated correctly.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       CompositorViewportPixelSizeTest) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_scaled_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* parent_iframe_node = root->child_at(0);
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site A ------- proxies for B\n"
+      "        +--Site B -- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://baz.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
+  RenderFrameProxyHost* proxy_to_parent =
+      nested_iframe_node->render_manager()->GetProxyToParent();
+  CrossProcessFrameConnector* connector =
+      proxy_to_parent->cross_process_frame_connector();
+  RenderWidgetHostViewBase* rwhv_nested =
+      static_cast<RenderWidgetHostViewBase*>(
+          nested_iframe_node->current_frame_host()
+              ->GetRenderWidgetHost()
+              ->GetView());
+
+  RenderFrameSubmissionObserver frame_observer(nested_iframe_node);
+  frame_observer.WaitForMetadataChange();
+
+  // Verify that applying a CSS scale transform does not impact the size of the
+  // content of the nested iframe.
+  // The screen_space_rect_in_dip may be off by 1 due to rounding. There is no
+  // good way to avoid this due to various device-scale-factor. (e.g. when
+  // dsf=3.375, ceil(round(50 * 3.375) / 3.375) = 51. Thus, we allow the screen
+  // size in dip to be off by 1 here.
+  EXPECT_NEAR(50, connector->screen_space_rect_in_dip().size().width(), 1);
+  EXPECT_NEAR(50, connector->screen_space_rect_in_dip().size().height(), 1);
+  EXPECT_EQ(gfx::Size(100, 100), rwhv_nested->GetViewBounds().size());
+  EXPECT_EQ(gfx::Size(100, 100), connector->local_frame_size_in_dip());
+  EXPECT_EQ(connector->local_frame_size_in_pixels(),
+            rwhv_nested->GetCompositorViewportPixelSize());
+}
+
+// Verify an OOPIF resize handler doesn't fire immediately after load without
+// the frame having been resized. See https://crbug.com/826457.
+// TODO(crbug.com/1278038): Test is very flaky on many platforms.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       DISABLED_NoResizeAfterIframeLoad) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+
+  FrameTreeNode* iframe = root->child_at(0);
+  GURL site_url =
+      embedded_test_server()->GetURL("b.com", "/page_with_resize_handler.html");
+  EXPECT_TRUE(NavigateToURLFromRenderer(iframe, site_url));
+  base::RunLoop().RunUntilIdle();
+
+  // Should be zero because the iframe only has its initial size from parent.
+  EXPECT_EQ(0, EvalJs(iframe->current_frame_host(), "resize_count;"));
+}
+
+// Test that the view bounds for an out-of-process iframe are set and updated
+// correctly, including accounting for local frame offsets in the parent and
+// scroll positions.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ViewBoundsInNestedFrameTest) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(a)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+  RenderWidgetHostViewBase* rwhv_root = static_cast<RenderWidgetHostViewBase*>(
+      root->current_frame_host()->GetRenderWidgetHost()->GetView());
+  ASSERT_EQ(1U, root->child_count());
+
+  FrameTreeNode* parent_iframe_node = root->child_at(0);
+  GURL site_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_positioned_frame.html"));
+  EXPECT_TRUE(NavigateToURLFromRenderer(parent_iframe_node, site_url));
+  RenderFrameSubmissionObserver frame_observer(shell()->web_contents());
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site A ------- proxies for B\n"
+      "        +--Site B -- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://baz.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* nested_iframe_node = parent_iframe_node->child_at(0);
+  RenderWidgetHostViewBase* rwhv_nested =
+      static_cast<RenderWidgetHostViewBase*>(
+          nested_iframe_node->current_frame_host()
+              ->GetRenderWidgetHost()
+              ->GetView());
+  WaitForHitTestData(nested_iframe_node->current_frame_host());
+
+  float scale_factor =
+      frame_observer.LastRenderFrameMetadata().page_scale_factor;
+
+  // Get the view bounds of the nested iframe, which should account for the
+  // relative offset of its direct parent within the root frame.
+  gfx::Rect bounds = rwhv_nested->GetViewBounds();
+
+  RenderFrameProxyHost* parent_iframe_proxy =
+      nested_iframe_node->render_manager()->GetProxyToParent();
+  auto interceptor = std::make_unique<SynchronizeVisualPropertiesInterceptor>(
+      parent_iframe_proxy);
+
+  // Scroll the parent frame downward to verify that the child rect gets updated
+  // correctly.
+  blink::WebMouseWheelEvent scroll_event(
+      blink::WebInputEvent::Type::kMouseWheel,
+      blink::WebInputEvent::kNoModifiers,
+      blink::WebInputEvent::GetStaticTimeStampForTests());
+
+  scroll_event.SetPositionInWidget(
+      std::floor((bounds.x() - rwhv_root->GetViewBounds().x() - 5) *
+                 scale_factor),
+      std::floor((bounds.y() - rwhv_root->GetViewBounds().y() - 5) *
+                 scale_factor));
+  scroll_event.delta_x = 0.0f;
+  scroll_event.delta_y = -30.0f;
+  scroll_event.phase = blink::WebMouseWheelEvent::kPhaseBegan;
+  rwhv_root->ProcessMouseWheelEvent(scroll_event, ui::LatencyInfo());
+  interceptor->WaitForRect();
+
+  // The precise amount of scroll for the first view position update is not
+  // deterministic, so this simply verifies that the OOPIF moved from its
+  // earlier position.
+  gfx::Rect update_rect = interceptor->last_rect();
+  EXPECT_LT(update_rect.y(), bounds.y() - rwhv_root->GetViewBounds().y());
+}
+
+// Verify that "scrolling" property on frame elements propagates to child frames
+// correctly.
+// Does not work on android since android has scrollbars overlaid.
+// TODO(bokan): Pretty soon most/all platforms will use overlay scrollbars. This
+// test should find a better way to check for scrollability. crbug.com/662196.
+// Flaky on Linux. crbug.com/790929.
+#if defined(OS_ANDROID) || defined(OS_LINUX) || defined(OS_CHROMEOS)
+#define MAYBE_FrameOwnerPropertiesPropagationScrolling \
+  DISABLED_FrameOwnerPropertiesPropagationScrolling
+#else
+#define MAYBE_FrameOwnerPropertiesPropagationScrolling \
+  FrameOwnerPropertiesPropagationScrolling
+#endif
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       MAYBE_FrameOwnerPropertiesPropagationScrolling) {
+#if defined(OS_MAC)
+  ui::test::ScopedPreferredScrollerStyle scroller_style_override(false);
+#endif
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_owner_properties_scrolling.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1u, root->child_count());
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* child = root->child_at(0);
+
+  // If the available client width within the iframe is smaller than the
+  // frame element's width, we assume there's a scrollbar.
+  // Also note that just comparing clientHeight and scrollHeight of the frame's
+  // document will not work.
+  auto has_scrollbar = [](RenderFrameHostImpl* rfh) {
+    int client_width = EvalJs(rfh, "document.body.clientWidth").ExtractInt();
+    const int kFrameElementWidth = 200;
+    return client_width < kFrameElementWidth;
+  };
+
+  auto set_scrolling_property = [](RenderFrameHostImpl* parent_rfh,
+                                   const std::string& value) {
+    EXPECT_TRUE(ExecJs(
+        parent_rfh,
+        base::StringPrintf("document.getElementById('child-1').setAttribute("
+                           "    'scrolling', '%s');",
+                           value.c_str())));
+  };
+
+  // Run the test over variety of parent/child cases.
+  GURL urls[] = {// Remote to remote.
+                 embedded_test_server()->GetURL("c.com", "/tall_page.html"),
+                 // Remote to local.
+                 embedded_test_server()->GetURL("a.com", "/tall_page.html"),
+                 // Local to remote.
+                 embedded_test_server()->GetURL("b.com", "/tall_page.html")};
+  const std::string scrolling_values[] = {"yes", "auto", "no"};
+
+  for (const auto& scrolling_value : scrolling_values) {
+    bool expect_scrollbar = scrolling_value != "no";
+    set_scrolling_property(root->current_frame_host(), scrolling_value);
+    for (const auto& url : urls) {
+      EXPECT_TRUE(NavigateToURLFromRenderer(child, url));
+      EXPECT_EQ(expect_scrollbar, has_scrollbar(child->current_frame_host()));
+    }
+  }
+}
+
+// Verify that "marginwidth" and "marginheight" properties on frame elements
+// propagate to child frames correctly.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       FrameOwnerPropertiesPropagationMargin) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_owner_properties_margin.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1u, root->child_count());
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* child = root->child_at(0);
+
+  EXPECT_EQ("10", EvalJs(child, "document.body.getAttribute('marginwidth');"));
+  EXPECT_EQ("50", EvalJs(child, "document.body.getAttribute('marginheight');"));
+
+  // Run the test over variety of parent/child cases.
+  GURL urls[] = {// Remote to remote.
+                 embedded_test_server()->GetURL("c.com", "/title2.html"),
+                 // Remote to local.
+                 embedded_test_server()->GetURL("a.com", "/title1.html"),
+                 // Local to remote.
+                 embedded_test_server()->GetURL("b.com", "/title2.html")};
+
+  int current_margin_width = 15;
+  int current_margin_height = 25;
+
+  // Before each navigation, we change the marginwidth and marginheight
+  // properties of the frame. We then check whether those properties are applied
+  // correctly after the navigation has completed.
+  for (const auto& url : urls) {
+    // Change marginwidth and marginheight before navigating.
+    EXPECT_TRUE(ExecJs(
+        root,
+        base::StringPrintf("var child = document.getElementById('child-1');"
+                           "child.setAttribute('marginwidth', '%d');"
+                           "child.setAttribute('marginheight', '%d');",
+                           current_margin_width, current_margin_height)));
+
+    EXPECT_TRUE(NavigateToURLFromRenderer(child, url));
+
+    EXPECT_EQ(base::NumberToString(current_margin_width),
+              EvalJs(child, "document.body.getAttribute('marginwidth');"));
+    EXPECT_EQ(base::NumberToString(current_margin_height),
+              EvalJs(child, "document.body.getAttribute('marginheight');"));
+
+    current_margin_width += 5;
+    current_margin_height += 10;
+  }
+}
+
+// Verify that "csp" property on frame elements propagates to child frames
+// correctly. See https://crbug.com/647588
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       FrameOwnerPropertiesPropagationCSP) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_owner_properties_csp.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // It is safe to obtain the root frame tree node here, as it doesn't change.
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1u, root->child_count());
+
+  // The document in the iframe is blocked by CSPEE. An error page is loaded, it
+  // stays in the process of the main document.
+  EXPECT_EQ(
+      " Site A\n"
+      "   +--Site A\n"
+      "Where A = http://a.com/",
+      DepictFrameTree(root));
+
+  FrameTreeNode* child = root->child_at(0);
+
+  EXPECT_EQ(
+      "object-src \'none\'",
+      EvalJs(root, "document.getElementById('child-1').getAttribute('csp');"));
+
+  // Run the test over variety of parent/child cases.
+  struct {
+    std::string csp_value;
+    GURL url;
+    bool should_block;
+  } testCases[]{
+      // Remote to remote.
+      {"default-src a.com",
+       embedded_test_server()->GetURL("c.com", "/title2.html"), true},
+      // Remote to local.
+      {"default-src b.com",
+       embedded_test_server()->GetURL("a.com", "/title1.html"), false},
+      // Local to remote.
+      {"img-src c.com", embedded_test_server()->GetURL("b.com", "/title2.html"),
+       true},
+  };
+
+  // Before each navigation, we change the csp property of the frame.
+  // We then check whether that property is applied
+  // correctly after the navigation has completed.
+  for (const auto& testCase : testCases) {
+    // Change csp before navigating.
+    EXPECT_TRUE(ExecJs(
+        root,
+        base::StringPrintf("document.getElementById('child-1').setAttribute("
+                           "    'csp', '%s');",
+                           testCase.csp_value.c_str())));
+
+    NavigateFrameToURL(child, testCase.url);
+    EXPECT_EQ(testCase.csp_value, child->csp_attribute()->header->header_value);
+    // TODO(amalika): add checks that the CSP replication takes effect
+
+    const url::Origin child_origin =
+        child->current_frame_host()->GetLastCommittedOrigin();
+
+    EXPECT_EQ(testCase.should_block, child_origin.opaque());
+    EXPECT_EQ(url::Origin::Create(testCase.url.DeprecatedGetOriginAsURL())
+                  .GetTupleOrPrecursorTupleIfOpaque(),
+              child_origin.GetTupleOrPrecursorTupleIfOpaque());
+  }
+}
+
+// This test verifies that changing the CSS visibility of a cross-origin
+// <iframe> is forwarded to its corresponding RenderWidgetHost and all other
+// RenderWidgetHosts corresponding to the nested cross-origin frame.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, CSSVisibilityChanged) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(b(c(d(d(a))))))"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  // Find all child RenderWidgetHosts.
+  std::vector<RenderWidgetHostImpl*> child_widget_hosts;
+  FrameTreeNode* first_cross_process_child =
+      web_contents()->GetPrimaryFrameTree().root()->child_at(0);
+  for (auto* ftn : web_contents()->GetPrimaryFrameTree().SubtreeNodes(
+           first_cross_process_child)) {
+    RenderFrameHostImpl* frame_host = ftn->current_frame_host();
+    if (!frame_host->is_local_root())
+      continue;
+
+    child_widget_hosts.push_back(frame_host->GetRenderWidgetHost());
+  }
+
+  // Ignoring the root, there is exactly 4 local roots and hence 5
+  // RenderWidgetHosts on the page.
+  EXPECT_EQ(4U, child_widget_hosts.size());
+
+  // Initially all the RenderWidgetHosts should be visible.
+  for (size_t index = 0; index < child_widget_hosts.size(); ++index) {
+    EXPECT_FALSE(child_widget_hosts[index]->is_hidden())
+        << "The RWH at distance " << index + 1U
+        << " from root RWH should not be hidden.";
+  }
+
+  std::string show_script =
+      "document.querySelector('iframe').style.visibility = 'visible';";
+  std::string hide_script =
+      "document.querySelector('iframe').style.visibility = 'hidden';";
+
+  // Define observers for notifications about hiding child RenderWidgetHosts.
+  std::vector<std::unique_ptr<RenderWidgetHostVisibilityObserver>>
+      hide_widget_host_observers(child_widget_hosts.size());
+  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
+    hide_widget_host_observers[index] =
+        std::make_unique<RenderWidgetHostVisibilityObserver>(
+            child_widget_hosts[index], false);
+  }
+
+  EXPECT_TRUE(ExecJs(shell(), hide_script));
+  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
+    EXPECT_TRUE(hide_widget_host_observers[index]->WaitUntilSatisfied())
+        << "Expected RenderWidgetHost at distance " << index + 1U
+        << " from root RenderWidgetHost to become hidden.";
+  }
+
+  // Define observers for notifications about showing child RenderWidgetHosts.
+  std::vector<std::unique_ptr<RenderWidgetHostVisibilityObserver>>
+      show_widget_host_observers(child_widget_hosts.size());
+  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
+    show_widget_host_observers[index] =
+        std::make_unique<RenderWidgetHostVisibilityObserver>(
+            child_widget_hosts[index], true);
+  }
+
+  EXPECT_TRUE(ExecJs(shell(), show_script));
+  for (size_t index = 0U; index < child_widget_hosts.size(); ++index) {
+    EXPECT_TRUE(show_widget_host_observers[index]->WaitUntilSatisfied())
+        << "Expected RenderWidgetHost at distance " << index + 1U
+        << " from root RenderWidgetHost to become shown.";
+  }
+}
+
+// This test verifies that hiding an OOPIF in CSS will stop generating
+// compositor frames for the OOPIF and any nested OOPIFs inside it. This holds
+// even when the whole page is shown.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       HiddenOOPIFWillNotGenerateCompositorFrames) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_two_frames.html"));
+  ASSERT_TRUE(NavigateToURL(shell(), main_url));
+  ASSERT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
+
+  GURL cross_site_url_b =
+      embedded_test_server()->GetURL("b.com", "/counter.html");
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url_b));
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), cross_site_url_b));
+
+  // Now inject code in the first frame to create a nested OOPIF.
+  RenderFrameHostCreatedObserver new_frame_created_observer(
+      shell()->web_contents(), 1);
+  ASSERT_TRUE(
+      ExecJs(root->child_at(0)->current_frame_host(),
+             "document.body.appendChild(document.createElement('iframe'));"));
+  new_frame_created_observer.Wait();
+
+  GURL cross_site_url_a =
+      embedded_test_server()->GetURL("a.com", "/counter.html");
+
+  // Navigate the nested frame.
+  TestFrameNavigationObserver observer(root->child_at(0)->child_at(0));
+  ASSERT_TRUE(ExecJs(root->child_at(0)->current_frame_host(),
+                     JsReplace("document.querySelector('iframe').src = $1",
+                               cross_site_url_a)));
+  observer.Wait();
+
+  RenderWidgetHostViewChildFrame* first_child_view =
+      static_cast<RenderWidgetHostViewChildFrame*>(
+          root->child_at(0)->current_frame_host()->GetView());
+  RenderWidgetHostViewChildFrame* second_child_view =
+      static_cast<RenderWidgetHostViewChildFrame*>(
+          root->child_at(1)->current_frame_host()->GetView());
+  RenderWidgetHostViewChildFrame* nested_child_view =
+      static_cast<RenderWidgetHostViewChildFrame*>(
+          root->child_at(0)->child_at(0)->current_frame_host()->GetView());
+
+  RenderFrameSubmissionObserver first_frame_counter(
+      first_child_view->host_->render_frame_metadata_provider());
+  RenderFrameSubmissionObserver second_frame_counter(
+      second_child_view->host_->render_frame_metadata_provider());
+  RenderFrameSubmissionObserver third_frame_counter(
+      nested_child_view->host_->render_frame_metadata_provider());
+
+  const int kFrameCountLimit = 20;
+
+  // Wait for a minimum number of compositor frames for the second frame.
+  while (second_frame_counter.render_frame_count() < kFrameCountLimit)
+    second_frame_counter.WaitForAnyFrameSubmission();
+  ASSERT_LE(kFrameCountLimit, second_frame_counter.render_frame_count());
+
+  // Now make sure all frames have roughly the counter value in the sense that
+  // no counter value is more than twice any other.
+  float ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
+                static_cast<float>(second_frame_counter.render_frame_count());
+  EXPECT_GT(2.5f, ratio + 1 / ratio) << "Ratio is: " << ratio;
+
+  ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
+          static_cast<float>(third_frame_counter.render_frame_count());
+  EXPECT_GT(2.5f, ratio + 1 / ratio) << "Ratio is: " << ratio;
+
+  // Make sure all views can become visible.
+  EXPECT_TRUE(first_child_view->CanBecomeVisible());
+  EXPECT_TRUE(second_child_view->CanBecomeVisible());
+  EXPECT_TRUE(nested_child_view->CanBecomeVisible());
+
+  // Hide the first frame and wait for the notification to be posted by its
+  // RenderWidgetHost.
+  RenderWidgetHostVisibilityObserver hide_observer(
+      root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), false);
+
+  // Hide the first frame.
+  ASSERT_TRUE(ExecJs(
+      shell(),
+      "document.getElementsByName('frame1')[0].style.visibility = 'hidden'"));
+  ASSERT_TRUE(hide_observer.WaitUntilSatisfied());
+  EXPECT_TRUE(first_child_view->FrameConnectorForTesting()->IsHidden());
+
+  // Verify that only the second view can become visible now.
+  EXPECT_FALSE(first_child_view->CanBecomeVisible());
+  EXPECT_TRUE(second_child_view->CanBecomeVisible());
+  EXPECT_FALSE(nested_child_view->CanBecomeVisible());
+
+  // Now hide and show the WebContents (to simulate a tab switch).
+  shell()->web_contents()->WasHidden();
+  shell()->web_contents()->WasShown();
+
+  first_frame_counter.ResetCounter();
+  second_frame_counter.ResetCounter();
+  third_frame_counter.ResetCounter();
+
+  // We expect the second counter to keep running.
+  while (second_frame_counter.render_frame_count() < kFrameCountLimit)
+    second_frame_counter.WaitForAnyFrameSubmission();
+  ASSERT_LT(kFrameCountLimit, second_frame_counter.render_frame_count() + 1);
+
+  // Verify that the counter for other two frames did not count much.
+  ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
+          static_cast<float>(second_frame_counter.render_frame_count());
+  EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
+
+  ratio = static_cast<float>(third_frame_counter.render_frame_count()) /
+          static_cast<float>(second_frame_counter.render_frame_count());
+  EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
+}
+
+// This test verifies that navigating a hidden OOPIF to cross-origin will not
+// lead to creating compositor frames for the new OOPIF renderer.
+IN_PROC_BROWSER_TEST_P(
+    SitePerProcessBrowserTest,
+    HiddenOOPIFWillNotGenerateCompositorFramesAfterNavigation) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_two_frames.html"));
+  ASSERT_TRUE(NavigateToURL(shell(), main_url));
+  ASSERT_EQ(shell()->web_contents()->GetLastCommittedURL(), main_url);
+
+  GURL cross_site_url_b =
+      embedded_test_server()->GetURL("b.com", "/counter.html");
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(0), cross_site_url_b));
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(root->child_at(1), cross_site_url_b));
+
+  // Hide the first frame and wait for the notification to be posted by its
+  // RenderWidgetHost.
+  RenderWidgetHostVisibilityObserver hide_observer(
+      root->child_at(0)->current_frame_host()->GetRenderWidgetHost(), false);
+
+  // Hide the first frame.
+  ASSERT_TRUE(ExecJs(
+      shell(),
+      "document.getElementsByName('frame1')[0].style.visibility = 'hidden'"));
+  ASSERT_TRUE(hide_observer.WaitUntilSatisfied());
+
+  // Now navigate the first frame to another OOPIF process.
+  TestFrameNavigationObserver navigation_observer(
+      root->child_at(0)->current_frame_host());
+  GURL cross_site_url_c =
+      embedded_test_server()->GetURL("c.com", "/counter.html");
+  ASSERT_TRUE(
+      ExecJs(web_contents(),
+             JsReplace("document.getElementsByName('frame1')[0].src = $1",
+                       cross_site_url_c)));
+  navigation_observer.Wait();
+
+  // Now investigate compositor frame creation.
+  RenderWidgetHostViewChildFrame* first_child_view =
+      static_cast<RenderWidgetHostViewChildFrame*>(
+          root->child_at(0)->current_frame_host()->GetView());
+
+  RenderWidgetHostViewChildFrame* second_child_view =
+      static_cast<RenderWidgetHostViewChildFrame*>(
+          root->child_at(1)->current_frame_host()->GetView());
+
+  EXPECT_FALSE(first_child_view->CanBecomeVisible());
+
+  RenderFrameSubmissionObserver first_frame_counter(
+      first_child_view->host_->render_frame_metadata_provider());
+  RenderFrameSubmissionObserver second_frame_counter(
+      second_child_view->host_->render_frame_metadata_provider());
+
+  const int kFrameCountLimit = 20;
+
+  // Wait for a certain number of swapped compositor frames generated for the
+  // second child view. During the same interval the first frame should not have
+  // swapped any compositor frames.
+  while (second_frame_counter.render_frame_count() < kFrameCountLimit)
+    second_frame_counter.WaitForAnyFrameSubmission();
+  ASSERT_LT(kFrameCountLimit, second_frame_counter.render_frame_count() + 1);
+
+  float ratio = static_cast<float>(first_frame_counter.render_frame_count()) /
+                static_cast<float>(second_frame_counter.render_frame_count());
+  EXPECT_GT(0.5f, ratio) << "Ratio is: " << ratio;
+}
+
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, ScreenCoordinates) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  FrameTreeNode* child = root->child_at(0);
+
+  const char* properties[] = {"screenX", "screenY", "outerWidth",
+                              "outerHeight"};
+
+  for (const char* property : properties) {
+    std::string script = base::StringPrintf("window.%s;", property);
+    int root_value = EvalJs(root, script).ExtractInt();
+    int child_value = EvalJs(child, script).ExtractInt();
+    EXPECT_EQ(root_value, child_value);
+  }
+}
+
+// Tests that an out-of-process iframe receives the visibilitychange event.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest, VisibilityChange) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  EXPECT_TRUE(
+      ExecJs(root->child_at(0),
+             "var event_fired = 0;\n"
+             "document.addEventListener('visibilitychange',\n"
+             "                          function() { event_fired++; });\n"));
+
+  shell()->web_contents()->WasHidden();
+
+  EXPECT_EQ(1, EvalJs(root->child_at(0), "event_fired"));
+
+  shell()->web_contents()->WasShown();
+
+  EXPECT_EQ(2, EvalJs(root->child_at(0), "event_fired"));
+}
+
+// This test verifies that the main-frame's page scale factor propagates to
+// the compositor layertrees in each of the child processes.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       PageScaleFactorPropagatesToOOPIFs) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b(c),d)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(2u, root->child_count());
+  FrameTreeNode* child_b = root->child_at(0);
+  FrameTreeNode* child_c = root->child_at(1);
+  ASSERT_EQ(1U, child_b->child_count());
+  FrameTreeNode* child_d = child_b->child_at(0);
+
+  ASSERT_TRUE(child_b);
+  ASSERT_TRUE(child_c);
+  ASSERT_TRUE(child_d);
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C D\n"
+      "   |--Site B ------- proxies for A C D\n"
+      "   |    +--Site C -- proxies for A B D\n"
+      "   +--Site D ------- proxies for A B C\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/\n"
+      "      C = http://c.com/\n"
+      "      D = http://d.com/",
+      DepictFrameTree(root));
+
+  RenderFrameSubmissionObserver observer_a(root);
+  RenderFrameSubmissionObserver observer_b(child_b);
+  RenderFrameSubmissionObserver observer_c(child_c);
+  RenderFrameSubmissionObserver observer_d(child_d);
+
+  // Monitor visual sync messages coming from the mainframe to make sure
+  // |is_pinch_gesture_active| goes true during the pinch gesture.
+  RenderFrameProxyHost* root_proxy_host =
+      child_d->render_manager()->GetProxyToParent();
+  auto interceptor_mainframe =
+      std::make_unique<SynchronizeVisualPropertiesInterceptor>(root_proxy_host);
+
+  // Monitor frame sync messages coming from child_b as it will need to
+  // relay them to child_d.
+  RenderFrameProxyHost* child_b_proxy_host =
+      child_c->render_manager()->GetProxyToParent();
+  auto interceptor_child_b =
+      std::make_unique<SynchronizeVisualPropertiesInterceptor>(
+          child_b_proxy_host);
+
+  // We need to observe a root frame submission to pick up the initial page
+  // scale factor.
+  observer_a.WaitForAnyFrameSubmission();
+
+  const float kPageScaleDelta = 2.f;
+  // On desktop systems we expect |current_page_scale| to be 1.f, but on
+  // Android it will typically be less than 1.f, and may take on arbitrary
+  // values.
+  float current_page_scale =
+      observer_a.LastRenderFrameMetadata().page_scale_factor;
+  float target_page_scale = current_page_scale * kPageScaleDelta;
+
+  SyntheticPinchGestureParams params;
+  auto* host = static_cast<RenderWidgetHostImpl*>(
+      root->current_frame_host()->GetRenderWidgetHost());
+  gfx::Rect bounds(host->GetView()->GetViewBounds().size());
+  // The synthetic gesture code expects a location in root-view coordinates.
+  params.anchor = gfx::PointF(bounds.CenterPoint());
+  // In SyntheticPinchGestureParams, |scale_factor| is really a delta.
+  params.scale_factor = kPageScaleDelta;
+#if defined(OS_MAC)
+  auto synthetic_pinch_gesture =
+      std::make_unique<SyntheticTouchpadPinchGesture>(params);
+#else
+  auto synthetic_pinch_gesture =
+      std::make_unique<SyntheticTouchscreenPinchGesture>(params);
+#endif
+
+  // Send pinch gesture and verify we receive the ack.
+  InputEventAckWaiter ack_waiter(host,
+                                 blink::WebInputEvent::Type::kGesturePinchEnd);
+  host->QueueSyntheticGesture(
+      std::move(synthetic_pinch_gesture),
+      base::BindOnce([](SyntheticGesture::Result result) {
+        EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+      }));
+  ack_waiter.Wait();
+
+  // Make sure all the page scale values behave as expected.
+  const float kScaleTolerance = 0.1f;
+  observer_a.WaitForPageScaleFactor(target_page_scale, kScaleTolerance);
+  observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
+  observer_c.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
+  observer_d.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
+
+  // The change in |is_pinch_gesture_active| that signals the end of the pinch
+  // gesture will occur sometime after the ack for GesturePinchEnd, so we need
+  // to wait for it from each renderer. If it's never seen, the test fails by
+  // timing out.
+  interceptor_mainframe->WaitForPinchGestureEnd();
+  EXPECT_TRUE(interceptor_mainframe->pinch_gesture_active_set());
+  EXPECT_TRUE(interceptor_mainframe->pinch_gesture_active_cleared());
+  interceptor_child_b->WaitForPinchGestureEnd();
+  EXPECT_TRUE(interceptor_child_b->pinch_gesture_active_set());
+  EXPECT_TRUE(interceptor_child_b->pinch_gesture_active_cleared());
+}
+
+// Test that the compositing scale factor for an out-of-process iframe are set
+// and updated correctly, including accounting for all intermediate transforms.
+// TODO(crbug.com/1164391): Flaky test.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       DISABLED_CompositingScaleFactorInNestedFrameTest) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_scaled_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* child_b = root->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_b, embedded_test_server()->GetURL(
+                   "b.com", "/frame_tree/page_with_transformed_iframe.html")));
+
+  ASSERT_EQ(1U, child_b->child_count());
+  FrameTreeNode* child_c = child_b->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_c, embedded_test_server()->GetURL(
+                   "c.com", "/frame_tree/page_with_scaled_frame.html")));
+
+  ASSERT_EQ(1U, child_c->child_count());
+  FrameTreeNode* child_d = child_c->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_d, embedded_test_server()->GetURL("d.com", "/simple_page.html")));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B C D\n"
+      "   +--Site B ------- proxies for A C D\n"
+      "        +--Site C -- proxies for A B D\n"
+      "             +--Site D -- proxies for A B C\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/\n"
+      "      C = http://c.com/\n"
+      "      D = http://d.com/",
+      DepictFrameTree(root));
+
+  // Wait for b.com's frame to have its compositing scale factor set to 0.5,
+  // which is the scale factor for b.com's iframe element in the main frame.
+  while (true) {
+    auto* rwh_b = child_b->current_frame_host()->GetRenderWidgetHost();
+    absl::optional<blink::VisualProperties> properties =
+        rwh_b->LastComputedVisualProperties();
+    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.5f)) {
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Wait for c.com's frame to have its compositing scale factor set to 0.5,
+  // which is the accumulated scale factor of c.com to the main frame obtained
+  // by multiplying the scale factor of c.com's iframe element (1 since
+  // transform is rotation only without scale) with the scale factor of its
+  // parent frame b.com (0.5).
+  while (true) {
+    auto* rwh_c = child_c->current_frame_host()->GetRenderWidgetHost();
+    absl::optional<blink::VisualProperties> properties =
+        rwh_c->LastComputedVisualProperties();
+    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.5f)) {
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Wait for d.com's frame to have its compositing scale factor set to 0.25,
+  // which is the accumulated scale factor of d.com to the main frame obtained
+  // by combining the scale factor of d.com's iframe element (0.5) with the
+  // scale factor of its parent d.com (0.5).
+  while (true) {
+    auto* rwh_d = child_d->current_frame_host()->GetRenderWidgetHost();
+    absl::optional<blink::VisualProperties> properties =
+        rwh_d->LastComputedVisualProperties();
+    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.25f)) {
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
+// Test that the compositing scale factor for an out-of-process iframe is set
+// to a non-zero value even if intermediate CSS transform has zero scale.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       CompositingScaleFactorWithZeroScaleTransform) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/frame_tree/page_with_scaled_frame.html"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = static_cast<WebContentsImpl*>(shell()->web_contents())
+                            ->GetPrimaryFrameTree()
+                            .root();
+
+  ASSERT_EQ(1U, root->child_count());
+  FrameTreeNode* child_b = root->child_at(0);
+
+  EXPECT_TRUE(NavigateToURLFromRenderer(
+      child_b,
+      embedded_test_server()->GetURL("b.com", "/frame_tree/simple_page.html")));
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  // Wait for b.com's frame to have its compositing scale factor set to 0.5,
+  // which is the scale factor for b.com's iframe element in the main frame.
+  while (true) {
+    auto* rwh_b = child_b->current_frame_host()->GetRenderWidgetHost();
+    absl::optional<blink::VisualProperties> properties =
+        rwh_b->LastComputedVisualProperties();
+    if (properties && cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.5f)) {
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+
+  // Set iframe transform scale to 0.
+  EXPECT_TRUE(
+      EvalJs(root->current_frame_host(),
+             "document.querySelector('iframe').style.transform = 'scale(0)'")
+          .error.empty());
+
+  // Wait for b.com frame's compositing scale factor to change, and check that
+  // the final value is non-zero.
+  while (true) {
+    auto* rwh_b = child_b->current_frame_host()->GetRenderWidgetHost();
+    absl::optional<blink::VisualProperties> properties =
+        rwh_b->LastComputedVisualProperties();
+    if (properties && !cc::MathUtil::IsFloatNearlyTheSame(
+                          properties->compositing_scale_factor, 0.5f)) {
+      EXPECT_GT(properties->compositing_scale_factor, 0.0f);
+      break;
+    }
+    base::RunLoop().RunUntilIdle();
+  }
+}
+
+// Check that when a frame changes a subframe's size twice and then sends a
+// postMessage to the subframe, the subframe's onmessage handler sees the new
+// size.  In particular, ensure that the postMessage won't get reordered with
+// the second resize, which might be throttled if the first resize is still in
+// progress. See https://crbug.com/828529.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       ResizeAndCrossProcessPostMessagePreserveOrder) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+
+  // Add an onmessage handler to the subframe to send back its width.
+  EXPECT_TRUE(ExecJs(root->child_at(0), R"(
+      window.addEventListener('message', function(event) {
+        domAutomationController.send(document.body.clientWidth);
+      });)"));
+
+  // Drop the visual properties ACKs from the child renderer.  To do this,
+  // unsubscribe the child's RenderWidgetHost from its
+  // RenderFrameMetadataProvider, which ensures that
+  // DidUpdateVisualProperties() won't be called on it, and the ACK won't be
+  // reset.  This simulates that the ACK for the first resize below does not
+  // arrive before the second resize IPC arrives from the
+  // parent, and that the second resize IPC early-exits in
+  // SynchronizeVisualProperties() due to the pending visual properties ACK.
+  RenderWidgetHostImpl* rwh =
+      root->child_at(0)->current_frame_host()->GetRenderWidgetHost();
+  rwh->render_frame_metadata_provider_.RemoveObserver(rwh);
+
+  // Now, resize the subframe twice from the main frame and send it a
+  // postMessage. The postMessage handler should see the second updated size.
+  EXPECT_EQ(700, EvalJs(root, R"(
+      var f = document.querySelector('iframe');
+      f.width = 500;
+      f.offsetTop; // force layout; this sends a resize IPC for width of 500.
+      f.width = 700;
+      f.offsetTop; // force layout; this sends a resize IPC for width of 700.
+      f.contentWindow.postMessage('foo', '*');)",
+                        EXECUTE_SCRIPT_USE_MANUAL_REPLY));
+}
+
+// This test verifies that when scrolling an OOPIF in a pinched-zoomed page,
+// that the scroll-delta matches the distance between TouchStart/End as seen
+// by the oopif, i.e. the oopif content 'sticks' to the finger during scrolling.
+// The relation is not exact, but should be close.
+IN_PROC_BROWSER_TEST_P(SitePerProcessBrowserTest,
+                       ScrollOopifInPinchZoomedPage) {
+  GURL main_url(embedded_test_server()->GetURL(
+      "a.com", "/cross_site_iframe_factory.html?a(b)"));
+  EXPECT_TRUE(NavigateToURL(shell(), main_url));
+
+  FrameTreeNode* root = web_contents()->GetPrimaryFrameTree().root();
+  ASSERT_EQ(1u, root->child_count());
+  FrameTreeNode* child = root->child_at(0);
+  ASSERT_TRUE(child);
+
+  EXPECT_EQ(
+      " Site A ------------ proxies for B\n"
+      "   +--Site B ------- proxies for A\n"
+      "Where A = http://a.com/\n"
+      "      B = http://b.com/",
+      DepictFrameTree(root));
+
+  // Make B scrollable. The call to document.write will erase the html inside
+  // the OOPIF, leaving just a vertical column of 'Hello's.
+  std::string script =
+      "var s = '<div>Hello</div>\\n';\n"
+      "document.write(s.repeat(200));";
+  EXPECT_TRUE(ExecJs(child, script));
+
+  RenderFrameSubmissionObserver observer_a(root);
+  RenderFrameSubmissionObserver observer_b(child);
+
+  // We need to observe a root frame submission to pick up the initial page
+  // scale factor.
+  observer_a.WaitForAnyFrameSubmission();
+
+  const float kPageScaleDelta = 2.f;
+  // On desktop systems we expect |current_page_scale| to be 1.f, but on
+  // Android it will typically be less than 1.f, and may take on arbitrary
+  // values.
+  float original_page_scale =
+      observer_a.LastRenderFrameMetadata().page_scale_factor;
+  float target_page_scale = original_page_scale * kPageScaleDelta;
+
+  SyntheticPinchGestureParams params;
+  auto* host = static_cast<RenderWidgetHostImpl*>(
+      root->current_frame_host()->GetRenderWidgetHost());
+  RenderWidgetHostViewBase* root_view = host->GetView();
+  RenderWidgetHostViewBase* child_view =
+      static_cast<RenderWidgetHostImpl*>(
+          child->current_frame_host()->GetRenderWidgetHost())
+          ->GetView();
+  gfx::Rect bounds(root_view->GetViewBounds().size());
+  // The synthetic gesture code expects a location in root-view coordinates.
+  params.anchor = gfx::PointF(bounds.CenterPoint().x(), 70.f);
+  // In SyntheticPinchGestureParams, |scale_factor| is really a delta.
+  params.scale_factor = kPageScaleDelta;
+#if defined(OS_MAC)
+  auto synthetic_pinch_gesture =
+      std::make_unique<SyntheticTouchpadPinchGesture>(params);
+#else
+  auto synthetic_pinch_gesture =
+      std::make_unique<SyntheticTouchscreenPinchGesture>(params);
+#endif
+
+  // Send pinch gesture and verify we receive the ack.
+  {
+    InputEventAckWaiter ack_waiter(
+        host, blink::WebInputEvent::Type::kGesturePinchEnd);
+    host->QueueSyntheticGesture(
+        std::move(synthetic_pinch_gesture),
+        base::BindOnce([](SyntheticGesture::Result result) {
+          EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+        }));
+    ack_waiter.Wait();
+  }
+
+  // Make sure all the page scale values behave as expected.
+  const float kScaleTolerance = 0.07f;
+  observer_a.WaitForPageScaleFactor(target_page_scale, kScaleTolerance);
+  observer_b.WaitForExternalPageScaleFactor(target_page_scale, kScaleTolerance);
+  float final_page_scale =
+      observer_a.LastRenderFrameMetadata().page_scale_factor;
+
+  // Verify scroll position of OOPIF.
+  float initial_child_scroll = EvalJs(child, "window.scrollY").ExtractDouble();
+
+  // Send touch-initiated gesture scroll sequence to OOPIF.
+  // TODO(wjmaclean): GetViewBounds() is broken for OOPIFs when PSF != 1.f, so
+  // we calculate it manually. This will need to be update when GetViewBounds()
+  // in RenderWidgetHostViewBase is fixed. See https://crbug.com/928825.
+  auto child_bounds = child_view->GetViewBounds();
+  gfx::PointF child_upper_left =
+      child_view->TransformPointToRootCoordSpaceF(gfx::PointF(0, 0));
+  gfx::PointF child_lower_right = child_view->TransformPointToRootCoordSpaceF(
+      gfx::PointF(child_bounds.width(), child_bounds.height()));
+  gfx::PointF scroll_start_location_in_screen =
+      gfx::PointF((child_upper_left.x() + child_lower_right.x()) / 2.f,
+                  child_lower_right.y() - 10);
+  const float kScrollDelta = 100.f;
+  gfx::PointF scroll_end_location_in_screen =
+      scroll_start_location_in_screen + gfx::Vector2dF(0, -kScrollDelta);
+
+  // Create touch move sequence with discrete touch moves. Include a brief
+  // pause at the end to avoid the scroll flinging.
+  std::string actions_template = R"HTML(
+      [{
+        "source" : "touch",
+        "actions" : [
+          { "name": "pointerDown", "x": %f, "y": %f},
+          { "name": "pointerMove", "x": %f, "y": %f},
+          { "name": "pause", "duration": 300 },
+          { "name": "pointerUp"}
+        ]
+      }]
+  )HTML";
+  std::string touch_move_sequence_json = base::StringPrintf(
+      actions_template.c_str(), scroll_start_location_in_screen.x(),
+      scroll_start_location_in_screen.y(), scroll_end_location_in_screen.x(),
+      scroll_end_location_in_screen.y());
+  base::JSONReader::ValueWithError parsed_json =
+      base::JSONReader::ReadAndReturnValueWithError(touch_move_sequence_json);
+  ASSERT_TRUE(parsed_json.value) << parsed_json.error_message;
+  ActionsParser actions_parser(std::move(*parsed_json.value));
+
+  ASSERT_TRUE(actions_parser.Parse());
+  auto synthetic_scroll_gesture =
+      SyntheticGesture::Create(actions_parser.gesture_params());
+
+  {
+    auto* child_host = static_cast<RenderWidgetHostImpl*>(
+        child->current_frame_host()->GetRenderWidgetHost());
+    InputEventAckWaiter ack_waiter(
+        child_host, blink::WebInputEvent::Type::kGestureScrollEnd);
+    host->QueueSyntheticGesture(
+        std::move(synthetic_scroll_gesture),
+        base::BindOnce([](SyntheticGesture::Result result) {
+          EXPECT_EQ(SyntheticGesture::GESTURE_FINISHED, result);
+        }));
+    ack_waiter.Wait();
+  }
+
+  // Verify new scroll position of OOPIF, should match touch sequence delta.
+  float expected_scroll_delta = kScrollDelta / final_page_scale;
+  float actual_scroll_delta =
+      EvalJs(child, "window.scrollY").ExtractDouble() - initial_child_scroll;
+
+  const float kScrollTolerance = 0.2f;
+  EXPECT_GT((1.f + kScrollTolerance) * expected_scroll_delta,
+            actual_scroll_delta);
+  EXPECT_LT((1.f - kScrollTolerance) * expected_scroll_delta,
+            actual_scroll_delta);
+}
+
+INSTANTIATE_TEST_SUITE_P(All,
+                         SitePerProcessHighDPIBrowserTest,
+                         testing::ValuesIn(RenderDocumentFeatureLevelValues()));
+}  // namespace content
diff --git a/content/browser/storage_partition_impl.cc b/content/browser/storage_partition_impl.cc
index 6af1039..f267d0a7 100644
--- a/content/browser/storage_partition_impl.cc
+++ b/content/browser/storage_partition_impl.cc
@@ -1181,23 +1181,14 @@
   StorageNotificationService* storage_notification_service =
       browser_context_->GetStorageNotificationService();
   if (storage_notification_service) {
-    // base::Unretained is safe to use because the BrowserContext is guaranteed
-    // to outlive QuotaManager. This is because BrowserContext outlives this
-    // StoragePartitionImpl, which destroys the QuotaManager on teardown.
-    base::RepeatingCallback<void(const blink::StorageKey)>
-        send_notification_function = base::BindRepeating(
-            [](StorageNotificationService* service,
-               const blink::StorageKey storage_key) {
-              GetUIThreadTaskRunner({})->PostTask(
-                  FROM_HERE,
-                  base::BindOnce(&StorageNotificationService::
-                                     MaybeShowStoragePressureNotification,
-                                 base::Unretained(service),
-                                 std::move(storage_key.origin())));
-            },
-            base::Unretained(storage_notification_service));
-
-    quota_manager_->SetStoragePressureCallback(send_notification_function);
+    // The weak ptr associated with the pressure notification callback will be
+    // created and evaluated by a task runner on the UI thread, as confirmed by
+    // the DCHECK's above, ensuring that the task runner does not attempt to run
+    // the callback in the case that the storage notification service is already
+    // destructed.
+    quota_manager_->SetStoragePressureCallback(
+        storage_notification_service
+            ->CreateThreadSafePressureNotificationCallback());
   }
 
   // Each consumer is responsible for registering its QuotaClient during
diff --git a/content/browser/webid/federated_auth_request_impl.cc b/content/browser/webid/federated_auth_request_impl.cc
index 1b4a171..3bd9acf 100644
--- a/content/browser/webid/federated_auth_request_impl.cc
+++ b/content/browser/webid/federated_auth_request_impl.cc
@@ -217,17 +217,23 @@
     IdpNetworkRequestManager::FetchStatus status,
     IdpNetworkRequestManager::Endpoints endpoints) {
   switch (status) {
-    case IdpNetworkRequestManager::FetchStatus::kWebIdNotSupported: {
-      CompleteRequest(RequestIdTokenStatus::kErrorFedCmNotSupportedByProvider,
+    case IdpNetworkRequestManager::FetchStatus::kHttpNotFoundError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingWellKnownHttpNotFound,
                       "");
       return;
     }
-    case IdpNetworkRequestManager::FetchStatus::kFetchError: {
-      CompleteRequest(RequestIdTokenStatus::kErrorFetchingWellKnown, "");
+    case IdpNetworkRequestManager::FetchStatus::kNoResponseError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingWellKnownNoResponse,
+                      "");
       return;
     }
     case IdpNetworkRequestManager::FetchStatus::kInvalidResponseError: {
-      CompleteRequest(RequestIdTokenStatus::kErrorInvalidWellKnown, "");
+      CompleteRequest(
+          RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse, "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kInvalidRequestError: {
+      NOTREACHED();
       return;
     }
     case IdpNetworkRequestManager::FetchStatus::kSuccess: {
@@ -246,7 +252,8 @@
       // For Mediated mode we require accounts, token and client ID endpoints.
       if (endpoints_.token.is_empty() || endpoints_.accounts.is_empty() ||
           endpoints_.client_id_metadata.is_empty()) {
-        CompleteRequest(RequestIdTokenStatus::kErrorInvalidWellKnown, "");
+        CompleteRequest(
+            RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse, "");
         return;
       }
       // TODO(kenrb): This has to be same-origin with the provider.
@@ -254,7 +261,8 @@
       if (!IdpUrlIsValid(endpoints_.token) ||
           !IdpUrlIsValid(endpoints_.accounts) ||
           !IdpUrlIsValid(endpoints_.client_id_metadata)) {
-        CompleteRequest(RequestIdTokenStatus::kError, "");
+        CompleteRequest(
+            RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse, "");
         return;
       }
       network_manager_->FetchClientIdMetadata(
@@ -267,13 +275,15 @@
     case RequestMode::kPermission: {
       // For Permission mode we require both accounts and token endpoints.
       if (endpoints_.idp.is_empty()) {
-        CompleteRequest(RequestIdTokenStatus::kErrorInvalidWellKnown, "");
+        CompleteRequest(
+            RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse, "");
         return;
       }
       // TODO(kenrb): This has to be same-origin with the provider.
       // https://crbug.com/1141125
       if (!IdpUrlIsValid(endpoints_.idp)) {
-        CompleteRequest(RequestIdTokenStatus::kError, "");
+        CompleteRequest(
+            RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse, "");
         return;
       }
 
@@ -344,13 +354,35 @@
 void FederatedAuthRequestImpl::OnClientIdMetadataResponseReceived(
     IdpNetworkRequestManager::FetchStatus status,
     IdpNetworkRequestManager::ClientIdMetadata data) {
-  // TODO(cbiesinger): check status argument to make sure fetching/parsing
-  // succeeded?
-  client_id_metadata_ = data;
-  network_manager_->SendAccountsRequest(
-      endpoints_.accounts,
-      base::BindOnce(&FederatedAuthRequestImpl::OnAccountsResponseReceived,
-                     weak_ptr_factory_.GetWeakPtr()));
+  switch (status) {
+    case IdpNetworkRequestManager::FetchStatus::kHttpNotFoundError: {
+      CompleteRequest(
+          RequestIdTokenStatus::kErrorFetchingClientIdMetadataHttpNotFound, "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kNoResponseError: {
+      CompleteRequest(
+          RequestIdTokenStatus::kErrorFetchingClientIdMetadataNoResponse, "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kInvalidResponseError: {
+      CompleteRequest(
+          RequestIdTokenStatus::kErrorFetchingClientIdMetadataInvalidResponse,
+          "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kInvalidRequestError: {
+      NOTREACHED();
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kSuccess: {
+      client_id_metadata_ = data;
+      network_manager_->SendAccountsRequest(
+          endpoints_.accounts,
+          base::BindOnce(&FederatedAuthRequestImpl::OnAccountsResponseReceived,
+                         weak_ptr_factory_.GetWeakPtr()));
+    }
+  }
 }
 
 void FederatedAuthRequestImpl::OnSigninApproved(
@@ -490,19 +522,26 @@
 }
 
 void FederatedAuthRequestImpl::OnAccountsResponseReceived(
-    IdpNetworkRequestManager::AccountsResponse status,
+    IdpNetworkRequestManager::FetchStatus status,
     IdpNetworkRequestManager::AccountList accounts,
     content::IdentityProviderMetadata idp_metadata) {
   switch (status) {
-    case IdpNetworkRequestManager::AccountsResponse::kNetError: {
-      CompleteRequest(RequestIdTokenStatus::kError, "");
+    case IdpNetworkRequestManager::FetchStatus::kHttpNotFoundError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingAccountsHttpNotFound,
+                      "");
       return;
     }
-    case IdpNetworkRequestManager::AccountsResponse::kInvalidResponseError: {
-      CompleteRequest(RequestIdTokenStatus::kErrorInvalidAccountsResponse, "");
+    case IdpNetworkRequestManager::FetchStatus::kNoResponseError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingAccountsNoResponse,
+                      "");
       return;
     }
-    case IdpNetworkRequestManager::AccountsResponse::kSuccess: {
+    case IdpNetworkRequestManager::FetchStatus::kInvalidResponseError: {
+      CompleteRequest(
+          RequestIdTokenStatus::kErrorFetchingAccountsInvalidResponse, "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kSuccess: {
       WebContents* rp_web_contents =
           WebContents::FromRenderFrameHost(render_frame_host_);
       DCHECK(!idp_web_contents_);
@@ -543,6 +582,9 @@
                          weak_ptr_factory_.GetWeakPtr()));
       return;
     }
+    case IdpNetworkRequestManager::FetchStatus::kInvalidRequestError: {
+      NOTREACHED();
+    }
   }
 }
 
@@ -569,22 +611,30 @@
 }
 
 void FederatedAuthRequestImpl::OnTokenResponseReceived(
-    IdpNetworkRequestManager::TokenResponse status,
+    IdpNetworkRequestManager::FetchStatus status,
     const std::string& id_token) {
   switch (status) {
-    case IdpNetworkRequestManager::TokenResponse::kNetError: {
-      CompleteRequest(RequestIdTokenStatus::kError, "");
+    case IdpNetworkRequestManager::FetchStatus::kHttpNotFoundError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingIdTokenHttpNotFound,
+                      "");
       return;
     }
-    case IdpNetworkRequestManager::TokenResponse::kInvalidRequestError: {
-      CompleteRequest(RequestIdTokenStatus::kErrorInvalidTokenResponse, "");
+    case IdpNetworkRequestManager::FetchStatus::kNoResponseError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingIdTokenNoResponse,
+                      "");
       return;
     }
-    case IdpNetworkRequestManager::TokenResponse::kInvalidResponseError: {
-      CompleteRequest(RequestIdTokenStatus::kErrorInvalidTokenResponse, "");
+    case IdpNetworkRequestManager::FetchStatus::kInvalidRequestError: {
+      CompleteRequest(RequestIdTokenStatus::kErrorFetchingIdTokenInvalidRequest,
+                      "");
       return;
     }
-    case IdpNetworkRequestManager::TokenResponse::kSuccess: {
+    case IdpNetworkRequestManager::FetchStatus::kInvalidResponseError: {
+      CompleteRequest(
+          RequestIdTokenStatus::kErrorFetchingIdTokenInvalidResponse, "");
+      return;
+    }
+    case IdpNetworkRequestManager::FetchStatus::kSuccess: {
       if (GetSharingPermissionContext()) {
         DCHECK_EQ(mode_, RequestMode::kMediated);
         // Grant sharing permission specific to *this account*.
diff --git a/content/browser/webid/federated_auth_request_impl.h b/content/browser/webid/federated_auth_request_impl.h
index 98f1f3ae..3d9790bc 100644
--- a/content/browser/webid/federated_auth_request_impl.h
+++ b/content/browser/webid/federated_auth_request_impl.h
@@ -82,11 +82,11 @@
   void OnTokenProvisionApproved(
       IdentityRequestDialogController::UserApproval approval);
   void OnAccountsResponseReceived(
-      IdpNetworkRequestManager::AccountsResponse status,
+      IdpNetworkRequestManager::FetchStatus status,
       IdpNetworkRequestManager::AccountList accounts,
       content::IdentityProviderMetadata idp_metadata);
   void OnAccountSelected(const std::string& account_id);
-  void OnTokenResponseReceived(IdpNetworkRequestManager::TokenResponse status,
+  void OnTokenResponseReceived(IdpNetworkRequestManager::FetchStatus status,
                                const std::string& id_token);
   void DispatchOneLogout();
   void OnLogoutCompleted();
diff --git a/content/browser/webid/federated_auth_request_impl_unittest.cc b/content/browser/webid/federated_auth_request_impl_unittest.cc
index 549e026..0087516 100644
--- a/content/browser/webid/federated_auth_request_impl_unittest.cc
+++ b/content/browser/webid/federated_auth_request_impl_unittest.cc
@@ -37,11 +37,9 @@
 using blink::mojom::RequestIdTokenStatus;
 using blink::mojom::RequestMode;
 using blink::mojom::RevokeStatus;
-using AccountsResponse = content::IdpNetworkRequestManager::AccountsResponse;
 using FetchStatus = content::IdpNetworkRequestManager::FetchStatus;
 using LogoutResponse = content::IdpNetworkRequestManager::LogoutResponse;
 using SigninResponse = content::IdpNetworkRequestManager::SigninResponse;
-using TokenResponse = content::IdpNetworkRequestManager::TokenResponse;
 using RevokeResponse = content::IdpNetworkRequestManager::RevokeResponse;
 using UserApproval = content::IdentityRequestDialogController::UserApproval;
 using AccountList = content::IdpNetworkRequestManager::AccountList;
@@ -113,9 +111,9 @@
 } MockPermissionConfiguration;
 
 typedef struct {
-  absl::optional<AccountsResponse> accounts_response;
+  absl::optional<FetchStatus> accounts_response;
   AccountList accounts;
-  absl::optional<TokenResponse> token_response;
+  absl::optional<FetchStatus> token_response;
 } MockMediatedConfiguration;
 
 typedef struct {
@@ -191,19 +189,20 @@
 
     {"Wellknown file not found",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorFedCmNotSupportedByProvider, kEmptyToken},
-     {kToken, UserApproval::kApproved, FetchStatus::kWebIdNotSupported,
+     {RequestIdTokenStatus::kErrorFetchingWellKnownHttpNotFound, kEmptyToken},
+     {kToken, UserApproval::kApproved, FetchStatus::kHttpNotFoundError,
       absl::nullopt, "", "", "", "", kPermissionNoop, kMediatedNoop}},
 
     {"Wellknown fetch error",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorFetchingWellKnown, kEmptyToken},
-     {kToken, UserApproval::kApproved, FetchStatus::kFetchError, absl::nullopt,
-      "", "", "", "", kPermissionNoop, kMediatedNoop}},
+     {RequestIdTokenStatus::kErrorFetchingWellKnownNoResponse, kEmptyToken},
+     {kToken, UserApproval::kApproved, FetchStatus::kNoResponseError,
+      absl::nullopt, "", "", "", "", kPermissionNoop, kMediatedNoop}},
 
     {"Error parsing wellknown for Permission mode",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kPermission},
-     {RequestIdTokenStatus::kErrorInvalidWellKnown, kEmptyToken},
+     {RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
+      kEmptyToken},
      {kToken, UserApproval::kApproved, FetchStatus::kInvalidResponseError,
       absl::nullopt, "", kAccountsEndpoint, kTokenEndpoint, "", kPermissionNoop,
       kMediatedNoop}},
@@ -267,21 +266,23 @@
 static const AuthRequestTestCase kMediatedTestCases[]{
     {"Error parsing wellknown for Mediated mode missing token endpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kErrorInvalidWellKnown, kEmptyToken},
+     {RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
+      kEmptyToken},
      {kToken, absl::nullopt, FetchStatus::kInvalidResponseError, absl::nullopt,
       kIdpEndpoint, kAccountsEndpoint, "", kClientIdMetadataEndpoint,
       kPermissionNoop, kMediatedNoop}},
 
     {"Error parsing wellknown for Mediated mode missing accounts endpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kErrorInvalidWellKnown, kEmptyToken},
+     {RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse,
+      kEmptyToken},
      {kToken, absl::nullopt, FetchStatus::kInvalidResponseError, absl::nullopt,
       kIdpEndpoint, "", kTokenEndpoint, kClientIdMetadataEndpoint,
       kPermissionNoop, kMediatedNoop}},
 
     {"Error reaching Accounts endpoint",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kError, kEmptyToken},
+     {RequestIdTokenStatus::kErrorFetchingAccountsNoResponse, kEmptyToken},
      {kEmptyToken,
       absl::nullopt,
       FetchStatus::kSuccess,
@@ -291,11 +292,11 @@
       kTokenEndpoint,
       kClientIdMetadataEndpoint,
       kPermissionNoop,
-      {AccountsResponse::kNetError, kAccounts, absl::nullopt}}},
+      {FetchStatus::kNoResponseError, kAccounts, absl::nullopt}}},
 
     {"Error parsing Accounts response",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
-     {RequestIdTokenStatus::kErrorInvalidAccountsResponse, kEmptyToken},
+     {RequestIdTokenStatus::kErrorFetchingAccountsInvalidResponse, kEmptyToken},
      {kToken,
       absl::nullopt,
       FetchStatus::kSuccess,
@@ -305,7 +306,7 @@
       kTokenEndpoint,
       kClientIdMetadataEndpoint,
       kPermissionNoop,
-      {AccountsResponse::kInvalidResponseError, kAccounts, absl::nullopt}}},
+      {FetchStatus::kInvalidResponseError, kAccounts, absl::nullopt}}},
 
     {"Successful Mediated flow",
      {kIdpTestOrigin, kClientId, kNonce, RequestMode::kMediated},
@@ -319,7 +320,7 @@
       kTokenEndpoint,
       kClientIdMetadataEndpoint,
       kPermissionNoop,
-      {AccountsResponse::kSuccess, kAccounts, TokenResponse::kSuccess}}},
+      {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}},
 };
 
 // Helper class for receiving the mojo method callback.
@@ -580,7 +581,7 @@
               }));
     }
 
-    if (conf.accounts_response == AccountsResponse::kSuccess &&
+    if (conf.accounts_response == FetchStatus::kSuccess &&
         !prefer_auto_sign_in) {
       // Expects a dialog if prefer_auto_sign_in is not set by RP. However,
       // even though the bit is set we may not exercise the AutoSignIn flow.
@@ -604,9 +605,8 @@
     }
 
     if (conf.token_response) {
-      auto delivered_token = conf.token_response == TokenResponse::kSuccess
-                                 ? token
-                                 : std::string();
+      auto delivered_token =
+          conf.token_response == FetchStatus::kSuccess ? token : std::string();
       EXPECT_CALL(*mock_request_manager_, SendTokenRequest(_, _, _, _))
           .WillOnce(Invoke(
               [=](const GURL& idp_signin_url, const std::string& account_id,
@@ -818,7 +818,7 @@
      kTokenEndpoint,
      kClientIdMetadataEndpoint,
      kPermissionNoop,
-     {AccountsResponse::kSuccess, kAccounts, TokenResponse::kSuccess}}};
+     {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}};
 
 static const AuthRequestTestCase kFailedMediatedSignUpTestCase{
     "Failed mediated flow with one account",
@@ -834,8 +834,7 @@
      kTokenEndpoint,
      kClientIdMetadataEndpoint,
      kPermissionNoop,
-     {AccountsResponse::kSuccess, kAccounts,
-      TokenResponse::kInvalidResponseError}}};
+     {FetchStatus::kSuccess, kAccounts, FetchStatus::kInvalidResponseError}}};
 
 static const AuthRequestTestCase kSuccessfulMediatedAutoSignInTestCase{
     "Successful mediated flow with one account",
@@ -851,7 +850,7 @@
      kTokenEndpoint,
      kClientIdMetadataEndpoint,
      kPermissionNoop,
-     {AccountsResponse::kSuccess, kAccounts, TokenResponse::kSuccess}}};
+     {FetchStatus::kSuccess, kAccounts, FetchStatus::kSuccess}}};
 
 TEST_F(BasicFederatedAuthRequestImplTest,
        LoginStateShouldBeSignUpForFirstTimeUser) {
diff --git a/content/browser/webid/idp_network_request_manager.cc b/content/browser/webid/idp_network_request_manager.cc
index 7aa5badd..03d6c43 100644
--- a/content/browser/webid/idp_network_request_manager.cc
+++ b/content/browser/webid/idp_network_request_manager.cc
@@ -148,6 +148,7 @@
 // adds parsed accounts to the |account_list|.
 bool ParseAccounts(const base::Value* accounts,
                    IdpNetworkRequestManager::AccountList& account_list) {
+  DCHECK(account_list.empty());
   if (!accounts->is_list())
     return false;
 
@@ -159,7 +160,7 @@
     if (parsed_account)
       account_list.push_back(parsed_account.value());
   }
-  return true;
+  return !account_list.empty();
 }
 
 absl::optional<SkColor> ParseCssColor(const std::string* value) {
@@ -194,6 +195,35 @@
   }
 }
 
+using FetchStatus = content::IdpNetworkRequestManager::FetchStatus;
+FetchStatus GetResponseError(network::SimpleURLLoader* url_loader,
+                             std::string* response_body) {
+  int response_code = -1;
+  auto* response_info = url_loader->ResponseInfo();
+  if (response_info && response_info->headers)
+    response_code = response_info->headers->response_code();
+
+  if (response_code == net::HTTP_NOT_FOUND)
+    return FetchStatus::kHttpNotFoundError;
+
+  if (!response_body)
+    return FetchStatus::kNoResponseError;
+
+  return FetchStatus::kSuccess;
+}
+
+FetchStatus GetParsingError(
+    const data_decoder::DataDecoder::ValueOrError& result) {
+  if (!result.value)
+    return FetchStatus::kInvalidResponseError;
+
+  auto& response = *result.value;
+  if (!response.is_dict())
+    return FetchStatus::kInvalidResponseError;
+
+  return FetchStatus::kSuccess;
+}
+
 }  // namespace
 
 IdpNetworkRequestManager::Endpoints::Endpoints() = default;
@@ -328,7 +358,7 @@
   std::string token_request_body = CreateTokenRequestBody(account, request);
   if (token_request_body.empty()) {
     std::move(token_request_callback_)
-        .Run(TokenResponse::kInvalidRequestError, std::string());
+        .Run(FetchStatus::kInvalidRequestError, std::string());
     return;
   }
 
@@ -418,22 +448,12 @@
 
 void IdpNetworkRequestManager::OnWellKnownLoaded(
     std::unique_ptr<std::string> response_body) {
-  int response_code = -1;
-  auto* response_info = url_loader_->ResponseInfo();
-  if (response_info && response_info->headers)
-    response_code = response_info->headers->response_code();
-
+  FetchStatus response_error =
+      GetResponseError(url_loader_.get(), response_body.get());
   url_loader_.reset();
 
-  if (response_code == net::HTTP_NOT_FOUND) {
-    std::move(idp_well_known_callback_)
-        .Run(FetchStatus::kWebIdNotSupported, Endpoints());
-    return;
-  }
-
-  if (!response_body) {
-    std::move(idp_well_known_callback_)
-        .Run(FetchStatus::kFetchError, Endpoints());
+  if (response_error != FetchStatus::kSuccess) {
+    std::move(idp_well_known_callback_).Run(response_error, Endpoints());
     return;
   }
 
@@ -445,22 +465,13 @@
 
 void IdpNetworkRequestManager::OnWellKnownParsed(
     data_decoder::DataDecoder::ValueOrError result) {
-  auto Fail = [&]() {
+  if (GetParsingError(result) == FetchStatus::kInvalidResponseError) {
     std::move(idp_well_known_callback_)
         .Run(FetchStatus::kInvalidResponseError, Endpoints());
-  };
-
-  if (!result.value) {
-    Fail();
     return;
   }
 
   auto& response = *result.value;
-  if (!response.is_dict()) {
-    Fail();
-    return;
-  }
-
   auto ExtractEndpoint = [&](const char* key) {
     const base::Value* endpoint = response.FindKey(key);
     if (!endpoint || !endpoint->is_string()) {
@@ -539,12 +550,13 @@
 
 void IdpNetworkRequestManager::OnAccountsRequestResponse(
     std::unique_ptr<std::string> response_body) {
+  FetchStatus response_error =
+      GetResponseError(url_loader_.get(), response_body.get());
   url_loader_.reset();
 
-  if (!response_body) {
+  if (response_error != FetchStatus::kSuccess) {
     std::move(accounts_request_callback_)
-        .Run(AccountsResponse::kNetError, AccountList(),
-             IdentityProviderMetadata());
+        .Run(response_error, AccountList(), IdentityProviderMetadata());
     return;
   }
 
@@ -558,21 +570,17 @@
     data_decoder::DataDecoder::ValueOrError result) {
   auto Fail = [&]() {
     std::move(accounts_request_callback_)
-        .Run(AccountsResponse::kInvalidResponseError, AccountList(),
+        .Run(FetchStatus::kInvalidResponseError, AccountList(),
              IdentityProviderMetadata());
   };
 
-  if (!result.value) {
+  if (GetParsingError(result) == FetchStatus::kInvalidResponseError) {
     Fail();
     return;
   }
 
-  auto& response = *result.value;
-  if (!response.is_dict()) {
-    Fail();
-    return;
-  }
   AccountList account_list;
+  auto& response = *result.value;
   const base::Value* accounts = response.FindKey(kAccountsKey);
   bool accounts_present = accounts && ParseAccounts(accounts, account_list);
 
@@ -587,17 +595,18 @@
     ParseIdentityProviderMetadata(*idp_metadata_value, idp_metadata);
 
   std::move(accounts_request_callback_)
-      .Run(AccountsResponse::kSuccess, std::move(account_list),
+      .Run(FetchStatus::kSuccess, std::move(account_list),
            std::move(idp_metadata));
 }
 
 void IdpNetworkRequestManager::OnTokenRequestResponse(
     std::unique_ptr<std::string> response_body) {
+  FetchStatus response_error =
+      GetResponseError(url_loader_.get(), response_body.get());
   url_loader_.reset();
 
-  if (!response_body) {
-    std::move(token_request_callback_)
-        .Run(TokenResponse::kNetError, std::string());
+  if (response_error != FetchStatus::kSuccess) {
+    std::move(token_request_callback_).Run(response_error, std::string());
     return;
   }
 
@@ -611,19 +620,15 @@
     data_decoder::DataDecoder::ValueOrError result) {
   auto Fail = [&]() {
     std::move(token_request_callback_)
-        .Run(TokenResponse::kInvalidResponseError, std::string());
+        .Run(FetchStatus::kInvalidResponseError, std::string());
   };
 
-  if (!result.value) {
+  if (GetParsingError(result) == FetchStatus::kInvalidResponseError) {
     Fail();
     return;
   }
 
   auto& response = *result.value;
-  if (!response.is_dict()) {
-    Fail();
-    return;
-  }
   const base::Value* id_token = response.FindKey(kIdTokenKey);
   bool token_present = id_token && id_token->is_string();
 
@@ -632,7 +637,7 @@
     return;
   }
   std::move(token_request_callback_)
-      .Run(TokenResponse::kSuccess, id_token->GetString());
+      .Run(FetchStatus::kSuccess, id_token->GetString());
 }
 
 void IdpNetworkRequestManager::OnRevokeResponse(
@@ -672,22 +677,13 @@
 
 void IdpNetworkRequestManager::OnClientIdMetadataLoaded(
     std::unique_ptr<std::string> response_body) {
-  int response_code = -1;
-  auto* response_info = url_loader_->ResponseInfo();
-  if (response_info && response_info->headers)
-    response_code = response_info->headers->response_code();
-
+  FetchStatus response_error =
+      GetResponseError(url_loader_.get(), response_body.get());
   url_loader_.reset();
 
-  if (response_code == net::HTTP_NOT_FOUND) {
+  if (response_error != FetchStatus::kSuccess) {
     std::move(client_metadata_callback_)
-        .Run(FetchStatus::kFetchError, ClientIdMetadata());
-    return;
-  }
-
-  if (!response_body) {
-    std::move(client_metadata_callback_)
-        .Run(FetchStatus::kFetchError, ClientIdMetadata());
+        .Run(response_error, ClientIdMetadata());
     return;
   }
 
@@ -699,22 +695,13 @@
 
 void IdpNetworkRequestManager::OnClientIdMetadataParsed(
     data_decoder::DataDecoder::ValueOrError result) {
-  auto Fail = [&]() {
+  if (GetParsingError(result) == FetchStatus::kInvalidResponseError) {
     std::move(client_metadata_callback_)
         .Run(FetchStatus::kInvalidResponseError, ClientIdMetadata());
-  };
-
-  if (!result.value) {
-    Fail();
     return;
   }
 
   auto& response = *result.value;
-  if (!response.is_dict()) {
-    Fail();
-    return;
-  }
-
   auto ExtractUrl = [&](const char* key) {
     const base::Value* endpoint = response.FindKey(key);
     if (!endpoint || !endpoint->is_string()) {
diff --git a/content/browser/webid/idp_network_request_manager.h b/content/browser/webid/idp_network_request_manager.h
index 7f365108..7ed622ca 100644
--- a/content/browser/webid/idp_network_request_manager.h
+++ b/content/browser/webid/idp_network_request_manager.h
@@ -61,9 +61,10 @@
  public:
   enum class FetchStatus {
     kSuccess,
-    kWebIdNotSupported,
-    kFetchError,
+    kHttpNotFoundError,
+    kNoResponseError,
     kInvalidResponseError,
+    kInvalidRequestError,
   };
 
   enum class SigninResponse {
@@ -73,19 +74,6 @@
     kInvalidResponseError,
   };
 
-  enum class AccountsResponse {
-    kSuccess,
-    kNetError,
-    kInvalidResponseError,
-  };
-
-  enum class TokenResponse {
-    kSuccess,
-    kNetError,
-    kInvalidRequestError,
-    kInvalidResponseError,
-  };
-
   enum class LogoutResponse {
     kSuccess,
     kError,
@@ -123,9 +111,9 @@
   using SigninRequestCallback =
       base::OnceCallback<void(SigninResponse, const std::string&)>;
   using AccountsRequestCallback = base::OnceCallback<
-      void(AccountsResponse, AccountList, IdentityProviderMetadata)>;
+      void(FetchStatus, AccountList, IdentityProviderMetadata)>;
   using TokenRequestCallback =
-      base::OnceCallback<void(TokenResponse, const std::string&)>;
+      base::OnceCallback<void(FetchStatus, const std::string&)>;
   using RevokeCallback = base::OnceCallback<void(RevokeResponse)>;
   using LogoutCallback = base::OnceCallback<void()>;
 
diff --git a/content/browser/webid/idp_network_request_manager_unittest.cc b/content/browser/webid/idp_network_request_manager_unittest.cc
index ca20f1c6..e5c2639 100644
--- a/content/browser/webid/idp_network_request_manager_unittest.cc
+++ b/content/browser/webid/idp_network_request_manager_unittest.cc
@@ -18,7 +18,7 @@
 #include "url/gurl.h"
 
 using AccountList = content::IdpNetworkRequestManager::AccountList;
-using AccountsResponse = content::IdpNetworkRequestManager::AccountsResponse;
+using FetchStatus = content::IdpNetworkRequestManager::FetchStatus;
 using AccountsRequestCallback =
     content::IdpNetworkRequestManager::AccountsRequestCallback;
 using RevokeResponse = content::IdpNetworkRequestManager::RevokeResponse;
@@ -43,18 +43,18 @@
 
   void TearDown() override { manager_.reset(); }
 
-  std::tuple<AccountsResponse, AccountList, IdentityProviderMetadata>
+  std::tuple<FetchStatus, AccountList, IdentityProviderMetadata>
   SendAccountsRequestAndWaitForResponse(const char* test_accounts) {
     GURL accounts_endpoint(kTestAccountsEndpoint);
     test_url_loader_factory().AddResponse(accounts_endpoint.spec(),
                                           test_accounts);
 
     base::RunLoop run_loop;
-    AccountsResponse parsed_accounts_response;
+    FetchStatus parsed_accounts_response;
     AccountList parsed_accounts;
     IdentityProviderMetadata parsed_idp_metadata;
     auto callback = base::BindLambdaForTesting(
-        [&](AccountsResponse response, AccountList accounts,
+        [&](FetchStatus response, AccountList accounts,
             IdentityProviderMetadata idp_metadata) {
           parsed_accounts_response = response;
           parsed_accounts = accounts;
@@ -106,13 +106,13 @@
   "accounts" : []
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_empty_account_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response);
   EXPECT_TRUE(accounts.empty());
 }
 
@@ -129,13 +129,13 @@
   ]
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_single_account_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ(1UL, accounts.size());
   EXPECT_EQ("1234", accounts[0].account_id);
 }
@@ -159,13 +159,13 @@
     }
   ]
   })";
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ(2UL, accounts.size());
   EXPECT_EQ("1234", accounts[0].account_id);
   EXPECT_EQ("5678", accounts[1].account_id);
@@ -183,13 +183,13 @@
   ]
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ("1234", accounts[0].account_id);
 }
 
@@ -199,14 +199,14 @@
       "email": "ken@idp.test",
       "name": "Ken R. Example"
     }]})";
-    AccountsResponse accounts_response;
+    FetchStatus accounts_response;
     AccountList accounts;
     IdentityProviderMetadata idp_metadata;
     std::tie(accounts_response, accounts, idp_metadata) =
         SendAccountsRequestAndWaitForResponse(
             test_accounts_missing_account_id_json);
 
-    EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+    EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response);
     EXPECT_TRUE(accounts.empty());
   }
   {
@@ -214,13 +214,13 @@
       "sub" : "1234",
       "name": "Ken R. Example"
     }]})";
-    AccountsResponse accounts_response;
+    FetchStatus accounts_response;
     AccountList accounts;
     IdentityProviderMetadata idp_metadata;
     std::tie(accounts_response, accounts, idp_metadata) =
         SendAccountsRequestAndWaitForResponse(test_accounts_missing_email_json);
 
-    EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+    EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response);
     EXPECT_TRUE(accounts.empty());
   }
   {
@@ -228,13 +228,13 @@
       "sub" : "1234",
       "email": "ken@idp.test"
     }]})";
-    AccountsResponse accounts_response;
+    FetchStatus accounts_response;
     AccountList accounts;
     IdentityProviderMetadata idp_metadata;
     std::tie(accounts_response, accounts, idp_metadata) =
         SendAccountsRequestAndWaitForResponse(test_accounts_missing_name_json);
 
-    EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+    EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response);
     EXPECT_TRUE(accounts.empty());
   }
 }
@@ -257,13 +257,13 @@
   ]
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_TRUE(accounts[0].picture.is_valid());
   EXPECT_EQ(GURL("https://idp.test/profile/1234"), accounts[0].picture);
   EXPECT_FALSE(accounts[1].picture.is_valid());
@@ -289,7 +289,7 @@
   for (auto& test_value : test_values) {
     const auto& accounts_json = TestAccountWithKeyValue("name", test_value);
 
-    AccountsResponse accounts_response;
+    FetchStatus accounts_response;
     AccountList accounts;
     IdentityProviderMetadata idp_metadata;
     std::tie(accounts_response, accounts, idp_metadata) =
@@ -303,26 +303,26 @@
 TEST_F(IdpNetworkRequestManagerTest, ParseAccountInvalid) {
   const auto* test_invalid_account_json = "{}";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_invalid_account_json);
 
-  EXPECT_EQ(AccountsResponse::kInvalidResponseError, accounts_response);
+  EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response);
   EXPECT_TRUE(accounts.empty());
 }
 
 TEST_F(IdpNetworkRequestManagerTest, ParseAccountMalformed) {
   const auto* test_invalid_account_json = "malformed_json";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_invalid_account_json);
 
-  EXPECT_EQ(AccountsResponse::kInvalidResponseError, accounts_response);
+  EXPECT_EQ(FetchStatus::kInvalidResponseError, accounts_response);
   EXPECT_TRUE(accounts.empty());
 }
 
@@ -341,13 +341,13 @@
   }
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ(SK_ColorBLUE, idp_metadata.brand_text_color);
   EXPECT_EQ(SkColorSetRGB(0xf0, 0xe0, 0xd0),
             idp_metadata.brand_background_color);
@@ -368,13 +368,13 @@
   }
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ(SkColorSetARGB(0xff, 0x20, 0x20, 0x20),
             idp_metadata.brand_background_color);
 }
@@ -393,13 +393,13 @@
   }
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ(absl::nullopt, idp_metadata.brand_background_color);
 }
 
@@ -419,13 +419,13 @@
   }
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ(SkColorSetRGB(0, 0, 0), idp_metadata.brand_background_color);
   EXPECT_EQ(absl::nullopt, idp_metadata.brand_text_color);
 }
@@ -445,13 +445,13 @@
   }
   })";
 
-  AccountsResponse accounts_response;
+  FetchStatus accounts_response;
   AccountList accounts;
   IdentityProviderMetadata idp_metadata;
   std::tie(accounts_response, accounts, idp_metadata) =
       SendAccountsRequestAndWaitForResponse(test_accounts_json);
 
-  EXPECT_EQ(AccountsResponse::kSuccess, accounts_response);
+  EXPECT_EQ(FetchStatus::kSuccess, accounts_response);
   EXPECT_EQ(absl::nullopt, idp_metadata.brand_background_color);
   EXPECT_EQ(absl::nullopt, idp_metadata.brand_text_color);
 }
diff --git a/content/browser/webid/webid_browsertest.cc b/content/browser/webid/webid_browsertest.cc
index 556da73..1819416c 100644
--- a/content/browser/webid/webid_browsertest.cc
+++ b/content/browser/webid/webid_browsertest.cc
@@ -301,8 +301,8 @@
   idp_server()->SetWellKnownResponseDetails({net::HTTP_NOT_FOUND, "", ""});
 
   std::string expected_error =
-      "a JavaScript error: \"NotSupportedError: The "
-      "indicated provider does not support FedCM.\"\n";
+      "a JavaScript error: \"NetworkError: The provider's .well-known "
+      "configuration cannot be found.\"\n";
   EXPECT_EQ(expected_error, EvalJs(shell(), GetBasicRequestString()).error);
 }
 
diff --git a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
index f6f3a77..73c6766d 100644
--- a/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
+++ b/content/public/android/javatests/src/org/chromium/content/browser/accessibility/WebContentsAccessibilityTreeTest.java
@@ -157,6 +157,888 @@
         performAriaTest("annotation-roles.html");
     }
 
+    @Test
+    @SmallTest
+    public void test_ariaAlertdialog() {
+        performAriaTest("aria-alertdialog.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaAlert() {
+        performAriaTest("aria-alert.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaApplication() {
+        performAriaTest("aria-application.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaArticle() {
+        performAriaTest("aria-article.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaAtomic() {
+        performAriaTest("aria-atomic.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaAutocomplete() {
+        performAriaTest("aria-autocomplete.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaBanner() {
+        performAriaTest("aria-banner.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaBusy() {
+        performAriaTest("aria-busy.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaButton() {
+        performAriaTest("aria-button.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaCell() {
+        performAriaTest("aria-cell.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaCheckbox() {
+        performAriaTest("aria-checkbox.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaChecked() {
+        performAriaTest("aria-checked.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaCode() {
+        performAriaTest("aria-code.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaColAttr() {
+        performAriaTest("aria-col-attr.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaColRowIndex() {
+        performAriaTest("aria-col-row-index.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaColRowIndexUndefined() {
+        performAriaTest("aria-col-row-index-undefined.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaColumnheader() {
+        performAriaTest("aria-columnheader.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaCombobox() {
+        performAriaTest("aria-combobox.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaComboboxImplicitHaspopup() {
+        performAriaTest("aria-combobox-implicit-haspopup.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaComboboxUneditable() {
+        performAriaTest("aria-combobox-uneditable.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaComplementary() {
+        performAriaTest("aria-complementary.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaContentinfo() {
+        performAriaTest("aria-contentinfo.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaControls() {
+        performAriaTest("aria-controls.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaCurrent() {
+        performAriaTest("aria-current.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDefinition() {
+        performAriaTest("aria-definition.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDescribedby() {
+        performAriaTest("aria-describedby.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDescription() {
+        performAriaTest("aria-description.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDetails() {
+        performAriaTest("aria-details.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDetailsMultiple() {
+        performAriaTest("aria-details-multiple.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDialog() {
+        performAriaTest("aria-dialog.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDirectory() {
+        performAriaTest("aria-directory.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDisabled() {
+        performAriaTest("aria-disabled.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDocument() {
+        performAriaTest("aria-document.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaDropeffect() {
+        performAriaTest("aria-dropeffect.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaEmphasis() {
+        performAriaTest("aria-emphasis.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaErrormessage() {
+        performAriaTest("aria-errormessage.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaExpanded() {
+        performAriaTest("aria-expanded.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaFigure() {
+        performAriaTest("aria-figure.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaFlowto() {
+        performAriaTest("aria-flowto.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaForm() {
+        performAriaTest("aria-form.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaGeneric() {
+        performAriaTest("aria-generic.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaGridcell() {
+        performAriaTest("aria-gridcell.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaGrid() {
+        performAriaTest("aria-grid.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaGroup() {
+        performAriaTest("aria-group.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaHaspopup() {
+        performAriaTest("aria-haspopup.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaHeading() {
+        performAriaTest("aria-heading.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaHiddenDescribedBy() {
+        performAriaTest("aria-hidden-described-by.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaHidden() {
+        performAriaTest("aria-hidden.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaHiddenIframe() {
+        performAriaTest("aria-hidden-iframe.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaHiddenLabelledBy() {
+        performAriaTest("aria-hidden-labelled-by.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaIllegalVal() {
+        performAriaTest("aria-illegal-val.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaImgChild() {
+        performAriaTest("aria-img-child.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaImg() {
+        performAriaTest("aria-img.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaInsertionDeletion() {
+        performAriaTest("aria-insertion-deletion.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaInvalid() {
+        performAriaTest("aria-invalid.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaKeyshortcuts() {
+        performAriaTest("aria-keyshortcuts.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaLabel() {
+        performAriaTest("aria-label.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaLabelledbyHeading() {
+        performAriaTest("aria-labelledby-heading.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaListboxAriaSelected() {
+        performAriaTest("aria-listbox-aria-selected.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaListboxDisabled() {
+        performAriaTest("aria-listbox-disabled.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaListbox() {
+        performAriaTest("aria-listbox.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaList() {
+        performAriaTest("aria-list.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaListitem() {
+        performAriaTest("aria-listitem.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaLive() {
+        performAriaTest("aria-live.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaLiveWithContent() {
+        performAriaTest("aria-live-with-content.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaLog() {
+        performAriaTest("aria-log.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMain() {
+        performAriaTest("aria-main.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMarquee() {
+        performAriaTest("aria-marquee.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMath() {
+        performAriaTest("aria-math.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMenubar() {
+        performAriaTest("aria-menubar.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMenu() {
+        performAriaTest("aria-menu.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMenuitemcheckbox() {
+        performAriaTest("aria-menuitemcheckbox.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMenuitem() {
+        performAriaTest("aria-menuitem.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMenuitemInGroup() {
+        performAriaTest("aria-menuitem-in-group.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMenuitemradio() {
+        performAriaTest("aria-menuitemradio.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMeter() {
+        performAriaTest("aria-meter.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMismatchedTableAttr() {
+        performAriaTest("aria-mismatched-table-attr.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMultiline() {
+        performAriaTest("aria-multiline.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaMultiselectable() {
+        performAriaTest("aria-multiselectable.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaNavigation() {
+        performAriaTest("aria-navigation.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaNone() {
+        performAriaTest("aria-none.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaNote() {
+        performAriaTest("aria-note.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaOptionComplexChildren() {
+        performAriaTest("aria-option-complex-children.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaOption() {
+        performAriaTest("aria-option.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaOrientation() {
+        performAriaTest("aria-orientation.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaOwnsFromDisplayNone() {
+        performAriaTest("aria-owns-from-display-none.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaOwns() {
+        performAriaTest("aria-owns.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaOwnsIgnored() {
+        performAriaTest("aria-owns-ignored.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaOwnsList() {
+        performAriaTest("aria-owns-list.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaPresentation() {
+        performAriaTest("aria-presentation.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaPresentationInList() {
+        performAriaTest("aria-presentation-in-list.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaPressed() {
+        performAriaTest("aria-pressed.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaProgressbar() {
+        performAriaTest("aria-progressbar.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRadiogroup() {
+        performAriaTest("aria-radiogroup.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRadio() {
+        performAriaTest("aria-radio.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaReadonly() {
+        performAriaTest("aria-readonly.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRegion() {
+        performAriaTest("aria-region.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRelevant() {
+        performAriaTest("aria-relevant.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRequired() {
+        performAriaTest("aria-required.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRoledescription() {
+        performAriaTest("aria-roledescription.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRowAttr() {
+        performAriaTest("aria-row-attr.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRowgroup() {
+        performAriaTest("aria-rowgroup.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRowheader() {
+        performAriaTest("aria-rowheader.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaRow() {
+        performAriaTest("aria-row.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaScrollbar() {
+        performAriaTest("aria-scrollbar.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSearch() {
+        performAriaTest("aria-search.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSelected() {
+        performAriaTest("aria-selected.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSeparator() {
+        performAriaTest("aria-separator.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSetsize() {
+        performAriaTest("aria-setsize.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSlider() {
+        performAriaTest("aria-slider.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSortAriaGrid() {
+        performAriaTest("aria-sort-aria-grid.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSortHtmlTable() {
+        performAriaTest("aria-sort-html-table.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaStatus() {
+        performAriaTest("aria-status.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaStrong() {
+        performAriaTest("aria-strong.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSubscript() {
+        performAriaTest("aria-subscript.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSuperscript() {
+        performAriaTest("aria-superscript.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaSwitch() {
+        performAriaTest("aria-switch.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTab() {
+        performAriaTest("aria-tab.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTable() {
+        performAriaTest("aria-table.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTablistAriaLevel() {
+        performAriaTest("aria-tablist-aria-level.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTablist() {
+        performAriaTest("aria-tablist.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTabNestedInLists() {
+        performAriaTest("aria-tab-nested-in-lists.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTabpanel() {
+        performAriaTest("aria-tabpanel.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTerm() {
+        performAriaTest("aria-term.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTextbox() {
+        performAriaTest("aria-textbox.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTextboxWithAriaTextboxChild() {
+        performAriaTest("aria-textbox-with-aria-textbox-child.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTextboxWithNonTextChildren() {
+        performAriaTest("aria-textbox-with-non-text-children.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTime() {
+        performAriaTest("aria-time.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTimer() {
+        performAriaTest("aria-timer.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTogglebutton() {
+        performAriaTest("aria-togglebutton.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaToolbar() {
+        performAriaTest("aria-toolbar.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTooltip() {
+        performAriaTest("aria-tooltip.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTreeDiscontinuous() {
+        performAriaTest("aria-tree-discontinuous.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTreegrid() {
+        performAriaTest("aria-treegrid.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTree() {
+        performAriaTest("aria-tree.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaTreeitemNestedInLists() {
+        performAriaTest("aria-treeitem-nested-in-lists.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaValuenow() {
+        performAriaTest("aria-valuenow.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaValuetext() {
+        performAriaTest("aria-valuetext.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_ariaVirtualcontent() {
+        performAriaTest("aria-virtualcontent.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_dpubRoles() {
+        performAriaTest("dpub-roles.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_graphicsRoles() {
+        performAriaTest("graphics-roles.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_hiddenDescribedBy() {
+        performAriaTest("hidden-described-by.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_hidden() {
+        performAriaTest("hidden.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_hiddenLabelledBy() {
+        performAriaTest("hidden-labelled-by.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_inputTextAriaPlaceholder() {
+        performAriaTest("input-text-aria-placeholder.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_presentational() {
+        performAriaTest("presentational.html");
+    }
+
+    @Test
+    @SmallTest
+    public void test_toggleButtonExpandCollapse() {
+        performAriaTest("toggle-button-expand-collapse.html");
+    }
+
     // ------------------ HTML TESTS ------------------ //
 
     @Test
diff --git a/content/public/browser/invalidate_type.h b/content/public/browser/invalidate_type.h
index b3afba06..0a14a6e6 100644
--- a/content/public/browser/invalidate_type.h
+++ b/content/public/browser/invalidate_type.h
@@ -15,19 +15,26 @@
 //   org.chromium.content_public.browser)
 // GENERATED_JAVA_PREFIX_TO_STRIP: INVALIDATE_TYPE_
 enum InvalidateTypes {
-  INVALIDATE_TYPE_URL           = 1 << 0,  // The URL has changed.
-  INVALIDATE_TYPE_TAB           = 1 << 1,  // The favicon, app icon, or crashed
-                                           // state changed.
-  INVALIDATE_TYPE_LOAD          = 1 << 2,  // The loading state has changed.
-  INVALIDATE_TYPE_TITLE         = 1 << 3,  // The title changed.
-  INVALIDATE_TYPE_AUDIO         = 1 << 4,  // The tab became audible or
-                                           // inaudible.
-                                           // TODO(crbug.com/846374):
-                                           // remove this.
-
-  INVALIDATE_TYPE_ALL           = (1 << 5) - 1,
+  INVALIDATE_TYPE_URL = 1 << 0,    // The URL has changed.
+  INVALIDATE_TYPE_TAB = 1 << 1,    // The favicon, app icon, or crashed
+                                   // state changed.
+  INVALIDATE_TYPE_LOAD = 1 << 2,   // The loading state has changed.
+  INVALIDATE_TYPE_TITLE = 1 << 3,  // The title changed.
+  INVALIDATE_TYPE_AUDIO = 1 << 4,  // The tab became audible or
+                                   // inaudible.
+                                   // TODO(crbug.com/846374):
+                                   // remove this.
+  INVALIDATE_TYPE_ALL = (1 << 5) - 1,
+  // Same as INVALIDATE_TYPE_ALL but this is caused by a navigation that used to
+  // get "ignored" (won't modify an existing NavigationEntry) because there are
+  // no NavigationEntries yet. Now that we will always have at least the initial
+  // NavigationEntry, these navigations won't get ignored anymore, but WebView
+  // still needs this info to ignore the NavigationStateChanged() call in some
+  // cases to avoid firing onPageFinished etc in more cases than it previously
+  // did. See also https://crbug.com/1277414.
+  INVALIDATE_TYPE_ALL_BUT_USED_TO_BE_IGNORED_DUE_TO_NULL_NAVIGATION_ENTRY =
+      (1 << 6) - 1,
 };
-
 }
 
 #endif  // CONTENT_PUBLIC_BROWSER_INVALIDATE_TYPE_H_
diff --git a/content/public/browser/navigation_details.h b/content/public/browser/navigation_details.h
index a51ef34..ed78e5b2 100644
--- a/content/public/browser/navigation_details.h
+++ b/content/public/browser/navigation_details.h
@@ -68,6 +68,16 @@
 
   // The HTTP status code for this entry..
   int http_status_code = 0;
+
+  // True if the navigation used to get "ignored" (committed but won't create a
+  // new NavigationEntry or modify an existing one) because there are no
+  // NavigationEntries yet. Now that we will always have at least the initial
+  // NavigationEntry, these navigations won't get ignored anymore, but Android
+  //  WebView still needs this info to ignore the NavigationStateChanged() call
+  // in some cases to avoid firing onPageFinished etc in more cases than it
+  // previously did.
+  // See also https://crbug.com/1277414.
+  bool used_to_be_ignored_due_to_null_navigation_entry = false;
 };
 
 // Provides the details for a NOTIFICATION_NAV_ENTRY_CHANGED notification.
diff --git a/content/public/browser/navigation_handle.h b/content/public/browser/navigation_handle.h
index 6bbed18..5a62684 100644
--- a/content/public/browser/navigation_handle.h
+++ b/content/public/browser/navigation_handle.h
@@ -266,6 +266,11 @@
   // exists.
   virtual GlobalRenderFrameHostId GetPreviousRenderFrameHostId() = 0;
 
+  // Returns the id of the RenderProcessHost this navigation is expected to
+  // commit in. The actual RenderProcessHost may change at commit time. It is
+  // only valid to call this before commit.
+  virtual int GetExpectedRenderProcessHostId() = 0;
+
   // Whether the navigation happened without changing document. Examples of
   // same document navigations are:
   // * reference fragment navigations
diff --git a/content/public/browser/storage_notification_service.h b/content/public/browser/storage_notification_service.h
index 9a2009c..31dfea6b 100644
--- a/content/public/browser/storage_notification_service.h
+++ b/content/public/browser/storage_notification_service.h
@@ -8,6 +8,15 @@
 #include "base/bind.h"
 #include "url/origin.h"
 
+namespace blink {
+class StorageKey;
+}
+
+namespace {
+using StoragePressureNotificationCallback =
+    base::RepeatingCallback<void(const blink::StorageKey)>;
+}
+
 namespace content {
 
 // This interface is used to create a connection between the storage layer and
@@ -24,12 +33,15 @@
 
   ~StorageNotificationService() = default;
 
-  // This pure virtual function should be implemented in the embedder layer
+  // These pure virtual functions should be implemented in the embedder layer
   // where calls to UI and notification code can be implemented. This closure
   // is passed to QuotaManager in StoragePartitionImpl, where it is called
   // when QuotaManager determines appropriate to alert the user that the device
   // is in a state of storage pressure.
-  virtual void MaybeShowStoragePressureNotification(const url::Origin) = 0;
+  virtual void MaybeShowStoragePressureNotification(
+      const blink::StorageKey) = 0;
+  virtual StoragePressureNotificationCallback
+  CreateThreadSafePressureNotificationCallback() = 0;
 };
 
 }  // namespace content
diff --git a/content/public/test/mock_navigation_handle.h b/content/public/test/mock_navigation_handle.h
index 5a5a803..67ca0e8 100644
--- a/content/public/test/mock_navigation_handle.h
+++ b/content/public/test/mock_navigation_handle.h
@@ -72,6 +72,7 @@
   }
   MOCK_METHOD0(GetFrameTreeNodeId, int());
   MOCK_METHOD0(GetPreviousRenderFrameHostId, GlobalRenderFrameHostId());
+  MOCK_METHOD(int, GetExpectedRenderProcessHostId, ());
   bool IsServedFromBackForwardCache() override {
     return is_served_from_bfcache_;
   }
diff --git a/content/public/test/web_transport_simple_test_server.cc b/content/public/test/web_transport_simple_test_server.cc
index 6bfe24b..2726c29 100644
--- a/content/public/test/web_transport_simple_test_server.cc
+++ b/content/public/test/web_transport_simple_test_server.cc
@@ -12,7 +12,6 @@
 #include "base/threading/thread_restrictions.h"
 #include "components/network_session_configurator/common/network_switches.h"
 #include "content/public/common/content_switches.h"
-#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h"
 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_backend.h"
 #include "net/tools/quic/quic_simple_server.h"
@@ -21,7 +20,7 @@
 namespace content {
 
 WebTransportSimpleTestServer::WebTransportSimpleTestServer() {
-  quic::QuicEnableVersion(quic::DefaultVersionForQuicTransport());
+  quic::QuicEnableVersion(quic::ParsedQuicVersion::RFCv1());
 }
 
 WebTransportSimpleTestServer::~WebTransportSimpleTestServer() {
@@ -44,7 +43,7 @@
   command_line->AppendSwitch(switches::kEnableQuic);
   command_line->AppendSwitchASCII(
       switches::kQuicVersion,
-      quic::AlpnForVersion(quic::DefaultVersionForQuicTransport()));
+      quic::AlpnForVersion(quic::ParsedQuicVersion::RFCv1()));
   // The value is calculated from net/data/ssl/certificates/quic-chain.pem.
   command_line->AppendSwitchASCII(
       network::switches::kIgnoreCertificateErrorsSPKIList,
diff --git a/content/services/auction_worklet/auction_v8_helper.cc b/content/services/auction_worklet/auction_v8_helper.cc
index 9a1bc04..1e9b1e1 100644
--- a/content/services/auction_worklet/auction_v8_helper.cc
+++ b/content/services/auction_worklet/auction_v8_helper.cc
@@ -42,6 +42,7 @@
 #include "v8/include/v8-primitive.h"
 #include "v8/include/v8-script.h"
 #include "v8/include/v8-template.h"
+#include "v8/include/v8-wasm.h"
 
 namespace auction_worklet {
 
@@ -441,6 +442,41 @@
   return result;
 }
 
+v8::MaybeLocal<v8::WasmModuleObject> AuctionV8Helper::CompileWasm(
+    const std::string& payload,
+    const GURL& src_url,
+    const DebugId* debug_id,
+    absl::optional<std::string>& error_out) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  v8::Isolate* v8_isolate = isolate();
+
+  DebugContextScope maybe_debug(inspector(), v8_isolate->GetCurrentContext(),
+                                debug_id, src_url.spec());
+
+  v8::TryCatch try_catch(isolate());
+  v8::MaybeLocal<v8::WasmModuleObject> result = v8::WasmModuleObject::Compile(
+      isolate(),
+      v8::MemorySpan<const uint8_t>(
+          reinterpret_cast<const uint8_t*>(payload.data()), payload.size()));
+  if (try_catch.HasCaught()) {
+    // WasmModuleObject::Compile doesn't know the URL, so FormatExceptionMessage
+    // would produce unhelpful message w/o that important bit of context.
+    error_out =
+        base::StrCat({src_url.spec(), " ",
+                      try_catch.Message().IsEmpty()
+                          ? "Unknown exception"
+                          : FormatValue(isolate(), try_catch.Message()->Get()),
+                      "."});
+  }
+  return result;
+}
+
+v8::MaybeLocal<v8::WasmModuleObject> AuctionV8Helper::CloneWasmModule(
+    v8::Local<v8::WasmModuleObject> in) {
+  return v8::WasmModuleObject::FromCompiledModule(isolate(),
+                                                  in->GetCompiledModule());
+}
+
 v8::MaybeLocal<v8::Value> AuctionV8Helper::RunScript(
     v8::Local<v8::Context> context,
     v8::Local<v8::UnboundScript> script,
diff --git a/content/services/auction_worklet/auction_v8_helper.h b/content/services/auction_worklet/auction_v8_helper.h
index 1021e84..73725195 100644
--- a/content/services/auction_worklet/auction_v8_helper.h
+++ b/content/services/auction_worklet/auction_v8_helper.h
@@ -34,6 +34,7 @@
 
 namespace v8 {
 class UnboundScript;
+class WasmModuleObject;
 }  // namespace v8
 
 namespace v8_inspector {
@@ -200,6 +201,27 @@
       const DebugId* debug_id,
       absl::optional<std::string>& error_out);
 
+  // Compiles the provided WASM module from bytecode. A context must be active
+  // for this method to be invoked, and the object would be created for it (but
+  // may be cloned efficiently for other contexts via CloneWasmModule). In case
+  // of an error sets `error_out`.
+  //
+  // Note that since the returned object is a JS Object, so to properly isolate
+  // different executions it should not be used directly but rather fresh copies
+  // should be made via CloneWasmModule.
+  v8::MaybeLocal<v8::WasmModuleObject> CompileWasm(
+      const std::string& payload,
+      const GURL& src_url,
+      const DebugId* debug_id,
+      absl::optional<std::string>& error_out);
+
+  // Creates a fresh object describing the same WASM module as `in`, which must
+  // not be empty. Can return an empty handle on an error.
+  //
+  // An execution context must be active, and the object will be created for it.
+  v8::MaybeLocal<v8::WasmModuleObject> CloneWasmModule(
+      v8::Local<v8::WasmModuleObject> in);
+
   // Binds a script and runs it in the passed in context, returning the result.
   // Note that the returned value could include references to objects or
   // functions contained within the context, so is likely not safe to use in
diff --git a/content/services/auction_worklet/auction_v8_helper_unittest.cc b/content/services/auction_worklet/auction_v8_helper_unittest.cc
index dd2b30a7..7ba8746 100644
--- a/content/services/auction_worklet/auction_v8_helper_unittest.cc
+++ b/content/services/auction_worklet/auction_v8_helper_unittest.cc
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/cxx17_backports.h"
 #include "base/strings/stringprintf.h"
 #include "base/synchronization/lock.h"
 #include "base/test/bind.h"
@@ -24,6 +25,7 @@
 #include "url/gurl.h"
 #include "v8/include/v8-context.h"
 #include "v8/include/v8-forward.h"
+#include "v8/include/v8-wasm.h"
 
 using testing::ElementsAre;
 using testing::HasSubstr;
@@ -31,6 +33,11 @@
 
 namespace auction_worklet {
 
+// The bytes of a minimal WebAssembly module, courtesy of
+// v8/test/cctest/test-api-wasm.cc
+const char kMinimalWasmModuleBytes[] = {0x00, 0x61, 0x73, 0x6d,
+                                        0x01, 0x00, 0x00, 0x00};
+
 class AuctionV8HelperTest : public testing::Test {
  public:
   explicit AuctionV8HelperTest(
@@ -109,6 +116,33 @@
             expect_success, std::move(done), result_out));
   }
 
+  bool CompileWasmOnV8ThreadAndWait(
+      scoped_refptr<AuctionV8Helper::DebugId> debug_id,
+      const GURL& url,
+      const std::string& body,
+      absl::optional<std::string>* error_out) {
+    bool success = false;
+    base::RunLoop run_loop;
+    helper_->v8_runner()->PostTask(
+        FROM_HERE,
+        base::BindOnce(
+            [](scoped_refptr<AuctionV8Helper> helper,
+               scoped_refptr<AuctionV8Helper::DebugId> debug_id, GURL url,
+               std::string body, bool* success_out,
+               absl::optional<std::string>* error_out, base::OnceClosure done) {
+              AuctionV8Helper::FullIsolateScope isolate_scope(helper.get());
+              v8::Context::Scope ctx(helper->scratch_context());
+              *success_out =
+                  !helper->CompileWasm(body, url, debug_id.get(), *error_out)
+                       .IsEmpty();
+              std::move(done).Run();
+            },
+            helper_, std::move(debug_id), url, body, &success, error_out,
+            run_loop.QuitClosure()));
+    run_loop.Run();
+    return success;
+  }
+
   void ConnectToDevToolsAgent(
       scoped_refptr<AuctionV8Helper::DebugId> debug_id,
       mojo::PendingReceiver<blink::mojom::DevToolsAgent> agent_receiver) {
@@ -1055,4 +1089,150 @@
   id->AbortDebuggerPauses();
 }
 
+TEST_F(AuctionV8HelperTest, CompileWasm) {
+  v8::Local<v8::Context> context = helper_->CreateContext();
+  v8::Context::Scope context_scope(context);
+
+  v8::Local<v8::WasmModuleObject> wasm_module;
+  absl::optional<std::string> compile_error;
+  ASSERT_TRUE(
+      helper_
+          ->CompileWasm(std::string(kMinimalWasmModuleBytes,
+                                    base::size(kMinimalWasmModuleBytes)),
+                        GURL("https://foo.test/"),
+                        /*debug_id=*/nullptr, compile_error)
+          .ToLocal(&wasm_module));
+  EXPECT_FALSE(compile_error.has_value());
+}
+
+TEST_F(AuctionV8HelperTest, CompileWasmError) {
+  v8::Local<v8::Context> context = helper_->CreateContext();
+  v8::Context::Scope context_scope(context);
+
+  v8::Local<v8::WasmModuleObject> wasm_module;
+  absl::optional<std::string> compile_error;
+  EXPECT_FALSE(helper_
+                   ->CompileWasm("not wasm", GURL("https://foo.test/"),
+                                 /*debug_id=*/nullptr, compile_error)
+                   .ToLocal(&wasm_module));
+  ASSERT_TRUE(compile_error.has_value());
+  EXPECT_THAT(compile_error.value(), StartsWith("https://foo.test/ "));
+  EXPECT_THAT(compile_error.value(),
+              HasSubstr("Uncaught CompileError: WasmModuleObject::Compile"));
+}
+
+TEST_F(AuctionV8HelperTest, CompileWasmDebug) {
+  const char kSession[] = "123-456";
+  // Need to use a separate thread for debugger stuff.
+  v8_scope_.reset();
+  helper_ = AuctionV8Helper::Create(AuctionV8Helper::CreateTaskRunner());
+
+  auto id = base::MakeRefCounted<AuctionV8Helper::DebugId>(helper_.get());
+
+  mojo::Remote<blink::mojom::DevToolsAgent> agent_remote;
+  ConnectToDevToolsAgent(id, agent_remote.BindNewPipeAndPassReceiver());
+
+  TestDevToolsAgentClient debug_client(std::move(agent_remote), kSession,
+                                       /*use_binary_protocol=*/false);
+
+  debug_client.RunCommandAndWaitForResult(
+      TestDevToolsAgentClient::Channel::kMain, 1, "Runtime.enable",
+      R"({"id":1,"method":"Runtime.enable","params":{}})");
+  debug_client.RunCommandAndWaitForResult(
+      TestDevToolsAgentClient::Channel::kMain, 2, "Debugger.enable",
+      R"({"id":2,"method":"Debugger.enable","params":{}})");
+
+  absl::optional<std::string> error_out;
+  EXPECT_TRUE(CompileWasmOnV8ThreadAndWait(
+      id, GURL("https://example.com"),
+      std::string(kMinimalWasmModuleBytes, base::size(kMinimalWasmModuleBytes)),
+      &error_out));
+  TestDevToolsAgentClient::Event script_parsed =
+      debug_client.WaitForMethodNotification("Debugger.scriptParsed");
+  const std::string* lang =
+      script_parsed.value.FindStringPath("params.scriptLanguage");
+  ASSERT_TRUE(lang);
+  EXPECT_EQ(*lang, "WebAssembly");
+
+  debug_client.WaitForMethodNotification("Runtime.executionContextDestroyed");
+  id->AbortDebuggerPauses();
+}
+
+TEST_F(AuctionV8HelperTest, CloneWasmModule) {
+  // Test proper CloneWasmModule() usage to prevent state persistence via
+  // WASM Module objects.
+
+  const char kScript[] = R"(
+    function probe(moduleObject) {
+      var result = moduleObject.weirdField ? moduleObject.weirdField : -1;
+      moduleObject.weirdField = 5;
+      return result;
+    }
+  )";
+
+  v8::Local<v8::Context> context = helper_->CreateContext();
+  v8::Context::Scope context_scope(context);
+
+  // Compile the WASM module...
+  v8::Local<v8::WasmModuleObject> wasm_module;
+  absl::optional<std::string> error_msg;
+  ASSERT_TRUE(
+      helper_
+          ->CompileWasm(std::string(kMinimalWasmModuleBytes,
+                                    base::size(kMinimalWasmModuleBytes)),
+                        GURL("https://foo.test/"),
+                        /*debug_id=*/nullptr, error_msg)
+          .ToLocal(&wasm_module));
+  EXPECT_FALSE(error_msg.has_value());
+
+  // And the test script.
+  v8::Local<v8::UnboundScript> script;
+  ASSERT_TRUE(helper_
+                  ->Compile(kScript, GURL("https://foo.test/"),
+                            /*debug_id=*/nullptr, error_msg)
+                  .ToLocal(&script));
+  EXPECT_FALSE(error_msg.has_value());
+
+  // Run the script a couple of times passing in the same module.
+  std::vector<v8::Local<v8::Value>> args;
+  args.push_back(wasm_module);
+  v8::Local<v8::Value> result;
+  std::vector<std::string> error_msgs;
+  ASSERT_TRUE(helper_
+                  ->RunScript(context, script,
+                              /*debug_id=*/nullptr, "probe", args, error_msgs)
+                  .ToLocal(&result));
+  EXPECT_TRUE(error_msgs.empty());
+  int int_result = 0;
+  ASSERT_TRUE(gin::ConvertFromV8(helper_->isolate(), result, &int_result));
+  EXPECT_EQ(-1, int_result);
+
+  ASSERT_TRUE(helper_
+                  ->RunScript(context, script,
+                              /*debug_id=*/nullptr, "probe", args, error_msgs)
+                  .ToLocal(&result));
+  EXPECT_TRUE(error_msgs.empty());
+  ASSERT_TRUE(gin::ConvertFromV8(helper_->isolate(), result, &int_result));
+  EXPECT_EQ(5, int_result);
+
+  // Nothing stick arounds if CloneWasmModule is consistently used, however.
+  args[0] = helper_->CloneWasmModule(wasm_module).ToLocalChecked();
+  ASSERT_TRUE(helper_
+                  ->RunScript(context, script,
+                              /*debug_id=*/nullptr, "probe", args, error_msgs)
+                  .ToLocal(&result));
+  EXPECT_TRUE(error_msgs.empty());
+  ASSERT_TRUE(gin::ConvertFromV8(helper_->isolate(), result, &int_result));
+  EXPECT_EQ(-1, int_result);
+
+  args[0] = helper_->CloneWasmModule(wasm_module).ToLocalChecked();
+  ASSERT_TRUE(helper_
+                  ->RunScript(context, script,
+                              /*debug_id=*/nullptr, "probe", args, error_msgs)
+                  .ToLocal(&result));
+  EXPECT_TRUE(error_msgs.empty());
+  ASSERT_TRUE(gin::ConvertFromV8(helper_->isolate(), result, &int_result));
+  EXPECT_EQ(-1, int_result);
+}
+
 }  // namespace auction_worklet
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index aca039df..5f4c1ab 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -1289,6 +1289,7 @@
     "../browser/site_per_process_browsertest.cc",
     "../browser/site_per_process_browsertest.h",
     "../browser/site_per_process_hit_test_browsertest.cc",
+    "../browser/site_per_process_layout_browsertest.cc",
     "../browser/site_per_process_mixed_content_browsertest.cc",
     "../browser/site_per_process_sad_frame_browsertest.cc",
     "../browser/site_per_process_scroll_browsertest.cc",
diff --git a/content/test/data/accessibility/aria/aria-alert-expected-android-external.txt b/content/test/data/accessibility/aria/aria-alert-expected-android-external.txt
new file mode 100644
index 0000000..2e34cfd
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-alert-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"This test is for aria role="alert"" viewIdResName:"ariaalert" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="alert", roleDescription="alert"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-alertdialog-expected-android-external.txt b/content/test/data/accessibility/aria/aria-alertdialog-expected-android-external.txt
new file mode 100644
index 0000000..f056c2ea3
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-alertdialog-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="alertDialog", roleDescription="alert_dialog"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-application-expected-android-external.txt b/content/test/data/accessibility/aria/aria-application-expected-android-external.txt
new file mode 100644
index 0000000..3258c9c1
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-application-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View viewIdResName:"testAriaApplication" actions:[AX_FOCUS] bundle:[chromeRole="application", roleDescription="application"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-article-expected-android-external.txt b/content/test/data/accessibility/aria/aria-article-expected-android-external.txt
new file mode 100644
index 0000000..ee1cd56
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-article-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"This is an ARIA article." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="article", roleDescription="article"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-atomic-expected-android-external.txt b/content/test/data/accessibility/aria/aria-atomic-expected-android-external.txt
new file mode 100644
index 0000000..0b4e7d6
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-atomic-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"This test is for aria-atomic="false"" viewIdResName:"ariaatomicfalse" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++View text:"This test is for aria-atomic="true"" viewIdResName:"ariaatomictrue" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++View text:"This test is for alert." viewIdResName:"alert" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="alert", roleDescription="alert"]
+++View text:"This test is for status." viewIdResName:"status" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="status", roleDescription="status"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-autocomplete-expected-android-external.txt b/content/test/data/accessibility/aria/aria-autocomplete-expected-android-external.txt
new file mode 100644
index 0000000..454b2ac2
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-autocomplete-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText text:"autocomplete=inline" canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300"]
+++++EditText text:"autocomplete=list" canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300"]
+++++EditText text:"autocomplete=both" canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300"]
+++++EditText text:"autocomplete=none" canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300"]
+++++EditText text:"No role with autocomplete=inline" clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-banner-expected-android-external.txt b/content/test/data/accessibility/aria/aria-banner-expected-android-external.txt
new file mode 100644
index 0000000..40e41ce1
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-banner-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"Chromium Browser" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="banner", roleDescription="banner"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-busy-expected-android-external.txt b/content/test/data/accessibility/aria/aria-busy-expected-android-external.txt
new file mode 100644
index 0000000..cc113f2
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-busy-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"Busy log" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++View text:"Not-busy log" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++TextView actions:[AX_FOCUS] bundle:[chromeRole="genericContainer", hint="plain div"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-button-expected-android-external.txt b/content/test/data/accessibility/aria/aria-button-expected-android-external.txt
new file mode 100644
index 0000000..be08f6c0
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-button-expected-android-external.txt
@@ -0,0 +1,21 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++Button text:"Button1" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++ToggleButton text:"Button2, On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Button3, Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++View text:"Button4" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="menu pop up button"]
+++Button text:"Button5" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++Button text:"Complex button " clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++ToggleButton text:"Complex toggle button , On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++++TextView text:"Complex toggle button " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++View text:"Complex pop up button " canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="menu pop up button"]
+++++TextView text:"Complex pop up button " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++View text:"Example haspopup" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="menu pop up button"]
+++View text:"Example haspopup" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="menu pop up button"]
+++View text:"Example haspopup" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="pop up button"]
+++View text:"Example haspopup" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="pop up button"]
+++View text:"Example haspopup" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="pop up button"]
+++View text:"Example haspopup" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="popUpButton", roleDescription="dialog pop up button"]
+++Button text:"Example haspopup" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++Button text:"Example haspopup" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-cell-expected-android-external.txt b/content/test/data/accessibility/aria/aria-cell-expected-android-external.txt
new file mode 100644
index 0000000..ef0a92c5
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-cell-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=2, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Chrome" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Blink" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-checkbox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-checkbox-expected-android-external.txt
new file mode 100644
index 0000000..7f3ec79
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-checkbox-expected-android-external.txt
@@ -0,0 +1,9 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++CheckBox text:"CheckBox1" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++CheckBox text:"CheckBox2" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++CheckBox text:"CheckBox3, Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++CheckBox text:"CheckBox4" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++CheckBox text:"Complex checkbox" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++++TextView text:"Complex " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++++TextView text:" checkbox" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-checked-expected-android-external.txt b/content/test/data/accessibility/aria/aria-checked-expected-android-external.txt
new file mode 100644
index 0000000..e93056b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-checked-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++CheckBox checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++CheckBox checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++CheckBox text:", Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-code-expected-android-external.txt b/content/test/data/accessibility/aria/aria-code-expected-android-external.txt
new file mode 100644
index 0000000..af6bbc3
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-code-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"role" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="code"]
+++TextView text:"element (no name)" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"include me" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="code"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-col-attr-expected-android-external.txt b/content/test/data/accessibility/aria/aria-col-attr-expected-android-external.txt
new file mode 100644
index 0000000..549d0d8
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-col-attr-expected-android-external.txt
@@ -0,0 +1,11 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=2, cols=4] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"cell 2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"cell 4" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"cell 5" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"cell 2" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"cell 3" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"cell 4" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"cell 5" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=3, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-col-row-index-expected-android-external.txt b/content/test/data/accessibility/aria/aria-col-row-index-expected-android-external.txt
new file mode 100644
index 0000000..313d965e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-col-row-index-expected-android-external.txt
@@ -0,0 +1,29 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=5, cols=4] actions:[AX_FOCUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="rowGroup"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row2 colheader2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++++View text:"row2 colheader3" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++++View text:"row2 colheader4" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++++View text:"row2 colheader5" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=3, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="rowGroup"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row3 col2" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row3 col3" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row3 col4" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row3 col5" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=3, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row4 col2" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row4 col3" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row4 col4" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row4 col5" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=3, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row5 col2" CollectionItemInfo:[rowIndex=3, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row5 col3" CollectionItemInfo:[rowIndex=3, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row5 col4" CollectionItemInfo:[rowIndex=3, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row5 col5" CollectionItemInfo:[rowIndex=3, rowSpan=1, colIndex=3, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row6 col2" CollectionItemInfo:[rowIndex=4, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row6 col3" CollectionItemInfo:[rowIndex=4, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row6 col4" CollectionItemInfo:[rowIndex=4, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row6 col5" CollectionItemInfo:[rowIndex=4, rowSpan=1, colIndex=3, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-col-row-index-undefined-expected-android-external.txt b/content/test/data/accessibility/aria/aria-col-row-index-undefined-expected-android-external.txt
new file mode 100644
index 0000000..4cae3e8
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-col-row-index-undefined-expected-android-external.txt
@@ -0,0 +1,16 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=3, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="rowGroup"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row1 colheader1" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++++View text:"row1 colheader2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++++View text:"row1 colheader3" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="rowGroup"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row2 col1" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row2 col2" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row2 col3" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"row3 col1" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row3 col2" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++++View text:"row3 col3" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-columnheader-expected-android-external.txt b/content/test/data/accessibility/aria/aria-columnheader-expected-android-external.txt
new file mode 100644
index 0000000..0530ac1e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-columnheader-expected-android-external.txt
@@ -0,0 +1,11 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=3, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Chrome" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Blink" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Safari" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"WebKit" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
new file mode 100644
index 0000000..96bec0f
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-combobox-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable scrollable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"State" viewIdResName:"state_label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++EditText canOpenPopUp clickable editable disabled focusable focused inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[CLEAR_FOCUS, CLICK, AX_FOCUS, COLLAPSE] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300", hint="State"]
+++ListView text:", 2 items" viewIdResName:"state_list" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Alabama, in list, item 1 of 2" viewIdResName:"state1" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Alaska, in list, item 2 of 2" viewIdResName:"state2" clickable focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-combobox-implicit-haspopup-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-implicit-haspopup-expected-android-external.txt
new file mode 100644
index 0000000..f6ca5f2
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-combobox-implicit-haspopup-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"ComboBoxGrouping" canOpenPopUp actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxGrouping"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++EditText canOpenPopUp clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300", hint="TextFieldWithComboBox"]
+++Spinner text:"ComboBoxMenuButton"" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comboBoxMenuButton"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt
new file mode 100644
index 0000000..d1229b8
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-combobox-uneditable-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"Choose a fruit, with text content" viewIdResName:"combo1-label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++Spinner text:"Choose a fruit, with text content" canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="comboBoxMenuButton"]
+++ListView text:", 3 items" viewIdResName:"listbox1" clickable CollectionInfo:[rows=3, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Apple, in list, item 1 of 3" viewIdResName:"combo1-0" clickable focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Banana, in list, item 2 of 3" viewIdResName:"combo1-1" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Cherry, in list, item 3 of 3" viewIdResName:"combo1-2" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-complementary-expected-android-external.txt b/content/test/data/accessibility/aria/aria-complementary-expected-android-external.txt
new file mode 100644
index 0000000..fb0bb77
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-complementary-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"This is ARIA role complementary." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="complementary", roleDescription="complementary"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-contentinfo-expected-android-external.txt b/content/test/data/accessibility/aria/aria-contentinfo-expected-android-external.txt
new file mode 100644
index 0000000..570ea1e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-contentinfo-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"This is ARIA role contentinfo." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="contentInfo", roleDescription="content information"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-controls-expected-android-external.txt b/content/test/data/accessibility/aria/aria-controls-expected-android-external.txt
new file mode 100644
index 0000000..9dc7320
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-controls-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menuBar", roleDescription="menu bar"]
+++++MenuItem text:"File" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Edit" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++View text:"File" viewIdResName:"filemenu" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++++MenuItem text:"New" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Open" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-current-expected-android-external.txt b/content/test/data/accessibility/aria/aria-current-expected-android-external.txt
new file mode 100644
index 0000000..7c0260e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-current-expected-android-external.txt
@@ -0,0 +1,28 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"null" contentDescription:"Section one" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-current.html#section1"]
+++++TextView text:"Section one" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"null" contentDescription:"Section two" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-current.html#section2"]
+++++TextView text:"Section two" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"null" contentDescription:"Section three, current location" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-current.html#section3"]
+++++TextView text:"Section three" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"\n" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="lineBreak"]
+++TextView text:"Section one heading" viewIdResName:"section1" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
+++TextView text:"Section two heading" viewIdResName:"section2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
+++TextView text:"Section three heading" viewIdResName:"section3" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
+++View text:"\n" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="lineBreak"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++TextView text:"Span 1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:"Span 2, current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:"Span 3" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++TextView text:"aria-current is true, current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is false" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is time, current time" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is date, current date" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is location, current location" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is step, current step" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is page, current page" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"aria-current is empty string" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-definition-expected-android-external.txt b/content/test/data/accessibility/aria/aria-definition-expected-android-external.txt
new file mode 100644
index 0000000..01f160b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-definition-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="definition", roleDescription="definition"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-describedby-expected-android-external.txt b/content/test/data/accessibility/aria/aria-describedby-expected-android-external.txt
new file mode 100644
index 0000000..9c6e8495
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-describedby-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText viewIdResName:"username" clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="Your username should be your email id"]
+++View text:"Your username should be your email id" viewIdResName:"username-tip" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tooltip", roleDescription="tooltip"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="mmddyy"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-description-expected-android-external.txt b/content/test/data/accessibility/aria/aria-description-expected-android-external.txt
new file mode 100644
index 0000000..29aa175
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-description-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"description" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="Text-description"]
+++TextView text:"both" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="Description from describedby"]
+++View text:"Description from describedby" viewIdResName:"desc1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tooltip", roleDescription="tooltip"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-details-expected-android-external.txt b/content/test/data/accessibility/aria/aria-details-expected-android-external.txt
new file mode 100644
index 0000000..4aad73b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-details-expected-android-external.txt
@@ -0,0 +1,10 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea", hasImage="true"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++TextView text:"Details" viewIdResName:"details" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="paragraph"]
+++++TextView text:"This " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:"text" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++++TextView text:" has details" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"Text details" viewIdResName:"details2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++Image text:"Image details" viewIdResName:"details3" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="image", hasImage="true", roleDescription="graphic"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-details-multiple-expected-android-external.txt b/content/test/data/accessibility/aria/aria-details-multiple-expected-android-external.txt
new file mode 100644
index 0000000..ca45526
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-details-multiple-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"Some " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"highlighted text" viewIdResName:"annotated-text" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="mark", roleDescription="highlight"]
+++TextView text:" example." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"Good job!" viewIdResName:"comment" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="comment", roleDescription="comment"]
+++View text:"Some footnote." viewIdResName:"footnote" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docFootnote", roleDescription="footnote"]
+++View text:"The meaning is." viewIdResName:"definition" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="definition", roleDescription="definition"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-dialog-expected-android-external.txt b/content/test/data/accessibility/aria/aria-dialog-expected-android-external.txt
new file mode 100644
index 0000000..9c9d4e5
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-dialog-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++Dialog text:"This is ARIA dialog." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="dialog", roleDescription="dialog"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-directory-expected-android-external.txt b/content/test/data/accessibility/aria/aria-directory-expected-android-external.txt
new file mode 100644
index 0000000..aa2abe1
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-directory-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="directory", roleDescription="directory"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-disabled-expected-android-external.txt b/content/test/data/accessibility/aria/aria-disabled-expected-android-external.txt
new file mode 100644
index 0000000..9ae9a31
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-disabled-expected-android-external.txt
@@ -0,0 +1,13 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++++EditText clickable editable disabled focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="textField"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++View disabled actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText clickable editable disabled focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="textField"]
+++++EditText clickable editable disabled focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="textField"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++++EditText clickable editable disabled focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="textField"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-document-expected-android-external.txt b/content/test/data/accessibility/aria/aria-document-expected-android-external.txt
new file mode 100644
index 0000000..9801a8a0
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-document-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"aria role document" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="document", roleDescription="document"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-dropeffect-expected-android-external.txt b/content/test/data/accessibility/aria/aria-dropeffect-expected-android-external.txt
new file mode 100644
index 0000000..aa0b7ce
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-dropeffect-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"copy" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"move" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"link" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"execute" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"popup" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"none(default)" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"link popup" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-emphasis-expected-android-external.txt b/content/test/data/accessibility/aria/aria-emphasis-expected-android-external.txt
new file mode 100644
index 0000000..9333639
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-emphasis-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"role" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="emphasis"]
+++TextView text:"element (no name)" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"include me" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="emphasis"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-errormessage-expected-android-external.txt b/content/test/data/accessibility/aria/aria-errormessage-expected-android-external.txt
new file mode 100644
index 0000000..7de6a108
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-errormessage-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="Invalid is true"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="Invalid is false"]
+++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="Invalid is not set"]
+++TextView text:"Error for invalid input" viewIdResName:"error1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"Error for input which is not invalid" viewIdResName:"error2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++TextView text:"Error for input with invalid not set" viewIdResName:"error3" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-expanded-expected-android-external.txt b/content/test/data/accessibility/aria/aria-expanded-expected-android-external.txt
new file mode 100644
index 0000000..c4b84bea
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-expanded-expected-android-external.txt
@@ -0,0 +1,9 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++++MenuItem text:"New" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Open" canOpenPopUp clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Save" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Quit" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-figure-expected-android-external.txt b/content/test/data/accessibility/aria/aria-figure-expected-android-external.txt
new file mode 100644
index 0000000..a79c3cea
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-figure-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"Figure" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="figure", roleDescription="graphic"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-flowto-expected-android-external.txt b/content/test/data/accessibility/aria/aria-flowto-expected-android-external.txt
new file mode 100644
index 0000000..b83d2901
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-flowto-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"current" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
+++View text:"next" viewIdResName:"next" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="footer", roleDescription="footer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-form-expected-android-external.txt b/content/test/data/accessibility/aria/aria-form-expected-android-external.txt
new file mode 100644
index 0000000..b9b845c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-form-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="form"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-generic-expected-android-external.txt b/content/test/data/accessibility/aria/aria-generic-expected-android-external.txt
new file mode 100644
index 0000000..fcd0cab
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-generic-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"content" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-grid-expected-android-external.txt b/content/test/data/accessibility/aria/aria-grid-expected-android-external.txt
new file mode 100644
index 0000000..276150c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-grid-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=2, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Chrome" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Blink" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-gridcell-expected-android-external.txt b/content/test/data/accessibility/aria/aria-gridcell-expected-android-external.txt
new file mode 100644
index 0000000..85816008
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-gridcell-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView text:", multiselectable, none selected." CollectionInfo:[rows=2, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Chrome" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Blink" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-group-expected-android-external.txt b/content/test/data/accessibility/aria/aria-group-expected-android-external.txt
new file mode 100644
index 0000000..6ab7082
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-group-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="group"]
+++++View text:"null" contentDescription:"Group Link1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", roleDescription="link"]
+++++++TextView text:"Group Link1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"null" contentDescription:"Group Link2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", roleDescription="link"]
+++++++TextView text:"Group Link2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-haspopup-expected-android-external.txt b/content/test/data/accessibility/aria/aria-haspopup-expected-android-external.txt
new file mode 100644
index 0000000..56d349e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-haspopup-expected-android-external.txt
@@ -0,0 +1,10 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
+++Spinner canOpenPopUp clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="comboBoxMenuButton"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-heading-expected-android-external.txt b/content/test/data/accessibility/aria/aria-heading-expected-android-external.txt
new file mode 100644
index 0000000..da1b8bd
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-heading-expected-android-external.txt
@@ -0,0 +1,9 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"ARIA Heading 1" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
+++TextView text:"ARIA Heading 2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 2"]
+++TextView text:"ARIA Heading 3" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 3"]
+++TextView text:"ARIA Heading 4" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 4"]
+++TextView text:"ARIA Heading 5" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 5"]
+++TextView text:"ARIA Heading 6" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 6"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++TextView text:"Heading" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 2"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-hidden-described-by-expected-android-external.txt b/content/test/data/accessibility/aria/aria-hidden-described-by-expected-android-external.txt
new file mode 100644
index 0000000..afcc236
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-hidden-described-by-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"span-A" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="span-1"]
+++TextView text:"span-B" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="span-2"]
+++TextView text:"span-C" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="span-3"]
+++TextView text:"span-D" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="span-4"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-hidden-expected-android-external.txt b/content/test/data/accessibility/aria/aria-hidden-expected-android-external.txt
new file mode 100644
index 0000000..cb098d9
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-hidden-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"blockDisplay" viewIdResName:"blockDisplay" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"blockDisplay Hiddenfalse" viewIdResName:"blockDisplayAriaHiddenFalse" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView viewIdResName:"noneDisplayAriaHiddenFalse" notVisibleToUser actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"blockDisplay Hiddentruefocusable" viewIdResName:"blockDisplayAriaHiddenTrueFocusable" focusable notVisibleToUser actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"noneDisplayParent Hiddenfalse" notVisibleToUser CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-hidden-iframe-expected-android-external.txt b/content/test/data/accessibility/aria/aria-hidden-iframe-expected-android-external.txt
new file mode 100644
index 0000000..ac89eb9
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-hidden-iframe-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++Button text:"Before" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++View notVisibleToUser actions:[AX_FOCUS] bundle:[chromeRole="iframe"]
+++++++View scrollable actions:[AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++++++++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++++++++Button text:"Inner" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++Button text:"After" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-hidden-labelled-by-expected-android-external.txt b/content/test/data/accessibility/aria/aria-hidden-labelled-by-expected-android-external.txt
new file mode 100644
index 0000000..e772b64
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-hidden-labelled-by-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"span-2" viewIdResName:"s2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"span-4" viewIdResName:"s3" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++View text:"span-1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"span-2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"span-3" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"span-4" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-illegal-val-expected-android-external.txt b/content/test/data/accessibility/aria/aria-illegal-val-expected-android-external.txt
new file mode 100644
index 0000000..0f3453a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-illegal-val-expected-android-external.txt
@@ -0,0 +1,25 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"Atomic illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++EditText clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Autocomplete illegal"]
+++View text:"Busy illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++View text:"Checked illegal" checkable checked clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++View text:"Current illegal, current item" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"Disabled illegal" disabled actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"Dropeffect illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"Expanded illegal" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++View text:"Grabbed illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++Button text:"Haspopup illegal" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++View text:"Invalid illegal" error:"Invalid entry" contentInvalid actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"Live illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++Dialog text:"Modal illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="dialog", roleDescription="dialog"]
+++EditText clickable editable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Multiline illegal"]
+++GridView text:"Multiselectable illegal, multiselectable, none selected." CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="grid", roleDescription="table"]
+++SeekBar text:"50, Orientation illegal" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
+++ToggleButton text:"Pressed illegal, On" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++EditText clickable editable disabled textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS] bundle:[chromeRole="textField", hint="Readonly illegal"]
+++View text:"Relevant illegal" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="log", roleDescription="log"]
+++EditText clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Required illegal"]
+++View text:", multiselectable, 1 of 1 selected." CollectionInfo:[hierarchical, rows=1, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++++View text:"Selected illegal" clickable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++GridView CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View text:"Sort illegal" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-img-child-expected-android-external.txt b/content/test/data/accessibility/aria/aria-img-child-expected-android-external.txt
new file mode 100644
index 0000000..7c32865
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-img-child-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea", hasImage="true"]
+++Image actions:[AX_FOCUS] bundle:[chromeRole="image", hasImage="true", roleDescription="graphic"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-img-expected-android-external.txt b/content/test/data/accessibility/aria/aria-img-expected-android-external.txt
new file mode 100644
index 0000000..7c32865
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-img-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea", hasImage="true"]
+++Image actions:[AX_FOCUS] bundle:[chromeRole="image", hasImage="true", roleDescription="graphic"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-insertion-deletion-expected-android-external.txt b/content/test/data/accessibility/aria/aria-insertion-deletion-expected-android-external.txt
new file mode 100644
index 0000000..83728cd
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-insertion-deletion-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="paragraph"]
+++++TextView text:"My favorite browser is " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"ABC" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="contentDeletion", roleDescription="deletion"]
+++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"Chrome" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="contentInsertion", roleDescription="insertion"]
+++++TextView text:"!" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-invalid-expected-android-external.txt b/content/test/data/accessibility/aria/aria-invalid-expected-android-external.txt
new file mode 100644
index 0000000..09dbd5c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-invalid-expected-android-external.txt
@@ -0,0 +1,13 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"invalid=true" error:"Invalid entry" contentInvalid actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++EditText text:"An error via invalid=spelling" clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++EditText text:"An error via invalid=grammar" clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++TextView text:"invalid=false" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"invalid=<empty>" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"invalid=<default>" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"invalid=unknown" error:"Invalid entry" contentInvalid actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="section"]
+++++EditText text:"1234" clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++EditText text:"a" clickable editable focusable inputType:209 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++EditText text:"a" clickable editable focusable inputType:209 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++EditText text:"a" clickable editable focusable inputType:209 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-keyshortcuts-expected-android-external.txt b/content/test/data/accessibility/aria/aria-keyshortcuts-expected-android-external.txt
new file mode 100644
index 0000000..6a47677
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-keyshortcuts-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++Button text:"Cut" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++Button text:"Copy" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++Button text:"Paste" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-label-expected-android-external.txt b/content/test/data/accessibility/aria/aria-label-expected-android-external.txt
new file mode 100644
index 0000000..6bcc748
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-label-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++CheckBox text:"aria label" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-labelledby-heading-expected-android-external.txt b/content/test/data/accessibility/aria/aria-labelledby-heading-expected-android-external.txt
new file mode 100644
index 0000000..c92f85c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-labelledby-heading-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="h2"]
+++TextView text:"h2" viewIdResName:"h2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 2"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-list-expected-android-external.txt b/content/test/data/accessibility/aria/aria-list-expected-android-external.txt
new file mode 100644
index 0000000..34a6187
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-list-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView CollectionInfo:[rows=3, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="list"]
+++++View text:"1" CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listItem"]
+++++View text:"2" CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listItem"]
+++++View text:"3" CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listItem"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-listbox-aria-selected-expected-android-external.txt b/content/test/data/accessibility/aria/aria-listbox-aria-selected-expected-android-external.txt
new file mode 100644
index 0000000..ecd1ff9
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-listbox-aria-selected-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView text:", multiselectable, 2 of 5 selected." clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++++View text:"Item 1, in list, item 1 of 5" viewIdResName:"it1" focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 2, in list, item 2 of 5" viewIdResName:"it2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 3, in list, item 3 of 5" viewIdResName:"it3" focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 4, in list, item 4 of 5" viewIdResName:"it4" focusable selected CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Item 5, in list, item 5 of 5" viewIdResName:"it5" focusable selected CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-listbox-disabled-expected-android-external.txt b/content/test/data/accessibility/aria/aria-listbox-disabled-expected-android-external.txt
new file mode 100644
index 0000000..0c7f460
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-listbox-disabled-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"Start of test: striped should have selected state" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 1"]
+++ListView text:", 4 items" clickable disabled CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++++View text:"Orange, in list, item 1 of 4" disabled focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Striped, in list, item 2 of 4" disabled focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Calico, in list, item 3 of 4" disabled focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++View text:"Black, in list, item 4 of 4" disabled focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++TextView text:"End of test" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-listbox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-listbox-expected-android-external.txt
new file mode 100644
index 0000000..ce971b0
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-listbox-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView text:", 4 items" clickable CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Item 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 2, in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", clickableScore="100", roleDescription="splitter"]
+++++View text:"Second group item 1, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Second group item 2, in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-listitem-expected-android-external.txt b/content/test/data/accessibility/aria/aria-listitem-expected-android-external.txt
new file mode 100644
index 0000000..407d1f42
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-listitem-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView CollectionInfo:[rows=2, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="list"]
+++++View text:"1" CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listItem"]
+++++View text:"2" CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listItem"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-live-expected-android-external.txt b/content/test/data/accessibility/aria/aria-live-expected-android-external.txt
new file mode 100644
index 0000000..32a039e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-live-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="alert", roleDescription="alert"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-live-with-content-expected-android-external.txt b/content/test/data/accessibility/aria/aria-live-with-content-expected-android-external.txt
new file mode 100644
index 0000000..db048c66
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-live-with-content-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"Off" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
+++View text:"Polite" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
+++View text:"Assertive" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-log-expected-android-external.txt b/content/test/data/accessibility/aria/aria-log-expected-android-external.txt
new file mode 100644
index 0000000..26ce2e6a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-log-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="log", roleDescription="log"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-main-expected-android-external.txt b/content/test/data/accessibility/aria/aria-main-expected-android-external.txt
new file mode 100644
index 0000000..a9ec597
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-main-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="main", roleDescription="main"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-marquee-expected-android-external.txt b/content/test/data/accessibility/aria/aria-marquee-expected-android-external.txt
new file mode 100644
index 0000000..c1a5960
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-marquee-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="marquee", roleDescription="marquee"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-math-expected-android-external.txt b/content/test/data/accessibility/aria/aria-math-expected-android-external.txt
new file mode 100644
index 0000000..ea80674
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-math-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"ARIA role math." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="math", roleDescription="math"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menu-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menu-expected-android-external.txt
new file mode 100644
index 0000000..b3fe52a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-menu-expected-android-external.txt
@@ -0,0 +1,10 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menuBar", roleDescription="menu bar"]
+++++MenuItem text:"File" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Edit" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"View" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++View text:"File" viewIdResName:"filemenu" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++++MenuItem text:"New" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Open" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Save" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Quit" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menubar-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menubar-expected-android-external.txt
new file mode 100644
index 0000000..6319eb4f
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-menubar-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menuBar", roleDescription="menu bar"]
+++++MenuItem text:"File" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Edit" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"View" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menuitem-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menuitem-expected-android-external.txt
new file mode 100644
index 0000000..0116223
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-menuitem-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++++MenuItem text:"File" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Edit" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++MenuItem text:"Complex menuitem" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
+++++++TextView text:"Complex " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++++++TextView text:" menuitem" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-android-external.txt
new file mode 100644
index 0000000..f2147559
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-menuitem-in-group-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="group"]
+++++MenuItem text:"Menu item" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItem", roleDescription="menu item"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android-external.txt
new file mode 100644
index 0000000..9e19ead
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-menuitemcheckbox-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++++MenuItem text:"Menu item 1" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
+++++MenuItem text:"Menu item 2" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
+++++MenuItem text:"Menu item 3, Partially Checked" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-menuitemradio-expected-android-external.txt b/content/test/data/accessibility/aria/aria-menuitemradio-expected-android-external.txt
new file mode 100644
index 0000000..d7501dd4
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-menuitemradio-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++++MenuItem text:"Menu item 1" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
+++++MenuItem text:"Menu item 2" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
+++++MenuItem text:"Menu item 3, Partially Checked" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-meter-expected-android-external.txt b/content/test/data/accessibility/aria/aria-meter-expected-android-external.txt
new file mode 100644
index 0000000..b9297ae
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-meter-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ProgressBar text:"half-way, funding" actions:[AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="meter", roleDescription="meter"]
+++ProgressBar text:"10, funding" RangeInfo:[current=10.0, min=0.0, max=100.0] actions:[AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="meter", roleDescription="meter"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-mismatched-table-attr-expected-android-external.txt b/content/test/data/accessibility/aria/aria-mismatched-table-attr-expected-android-external.txt
new file mode 100644
index 0000000..1ce11d71
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-mismatched-table-attr-expected-android-external.txt
@@ -0,0 +1,12 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=3, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Column 1" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Column 2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Column 3" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Cell A2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Cell A3" focusable CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Cell B1" focusable CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Cell B2" focusable CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=1, colSpan=1] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-multiline-expected-android-external.txt b/content/test/data/accessibility/aria/aria-multiline-expected-android-external.txt
new file mode 100644
index 0000000..f3464d3
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-multiline-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField"]
+++EditText clickable editable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-multiselectable-expected-android-external.txt b/content/test/data/accessibility/aria/aria-multiselectable-expected-android-external.txt
new file mode 100644
index 0000000..cae65089
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-multiselectable-expected-android-external.txt
@@ -0,0 +1,11 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView text:"My Listbox, multiselectable, none selected. 4 items" clickable focusable CollectionInfo:[rows=4, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Example 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 2, in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 3, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 4, in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++ListView text:"My Listbox, multiselectable, 2 of 4 selected." clickable focusable CollectionInfo:[rows=4, cols=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Example 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 2, in list, item 2 of 4" clickable focusable selected CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 3, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Example 4, in list, item 4 of 4" clickable focusable selected CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-navigation-expected-android-external.txt b/content/test/data/accessibility/aria/aria-navigation-expected-android-external.txt
new file mode 100644
index 0000000..c772649d
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-navigation-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="navigation", roleDescription="navigation"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-none-expected-android-external.txt b/content/test/data/accessibility/aria/aria-none-expected-android-external.txt
new file mode 100644
index 0000000..6c6f1c5b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-none-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"With aria-role none" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-note-expected-android-external.txt b/content/test/data/accessibility/aria/aria-note-expected-android-external.txt
new file mode 100644
index 0000000..7ebceea
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-note-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="note", roleDescription="note"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-option-complex-children-expected-android-external.txt b/content/test/data/accessibility/aria/aria-option-complex-children-expected-android-external.txt
new file mode 100644
index 0000000..ebb65f1
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-option-complex-children-expected-android-external.txt
@@ -0,0 +1,10 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView text:", 2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++++View text:"label-WAI-ARIA 1.1, in list, item 1 of 2" focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++++View text:"null" contentDescription:"href-WAI-ARIA 1.1" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", hint="title-WAI-ARIA 1.1", roleDescription="link", targetUrl="https://www.w3.org/TR/wai-aria-practices-1.1/"]
+++++++++TextView text:"href-WAI-ARIA 1.1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++TextView text:"title-https://www.w3.org/TR/wai-aria-practices-1.1/" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++++++Button text:"Close" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++View text:"1234567, in list, item 2 of 2" focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption"]
+++++++TextView text:"href-WAI-ARIA 1.1 " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++Button text:"Close" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-option-expected-android-external.txt b/content/test/data/accessibility/aria/aria-option-expected-android-external.txt
new file mode 100644
index 0000000..b334b0e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-option-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView text:", 5 items" clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"option 1, in list, item 1 of 5" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"label 2, in list, item 2 of 5" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"caterpillar, in list, item 3 of 5" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"catfish, in list, item 4 of 5" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"medusa, in list, item 5 of 5" clickable focusable CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-orientation-expected-android-external.txt b/content/test/data/accessibility/aria/aria-orientation-expected-android-external.txt
new file mode 100644
index 0000000..dc50ff86
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-orientation-expected-android-external.txt
@@ -0,0 +1,37 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View canOpenPopUp actions:[AX_FOCUS] bundle:[chromeRole="comboBoxGrouping"]
+++View canOpenPopUp actions:[AX_FOCUS] bundle:[chromeRole="comboBoxGrouping"]
+++View canOpenPopUp actions:[AX_FOCUS] bundle:[chromeRole="comboBoxGrouping"]
+++ListView clickable CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++ListView clickable CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++ListView clickable CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menu", roleDescription="menu"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menuBar", roleDescription="menu bar"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menuBar", roleDescription="menu bar"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="menuBar", roleDescription="menu bar"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="radioGroup", roleDescription="radio group"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="radioGroup", roleDescription="radio group"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="radioGroup", roleDescription="radio group"]
+++View text:"50" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="scrollBar", roleDescription="scroll bar"]
+++View text:"50" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="scrollBar", roleDescription="scroll bar"]
+++View text:"50" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="scrollBar", roleDescription="scroll bar"]
+++SeekBar text:"50" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
+++SeekBar text:"50" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
+++SeekBar text:"50" clickable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="toolbar", roleDescription="toolbar"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="toolbar", roleDescription="toolbar"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="toolbar", roleDescription="toolbar"]
+++View CollectionInfo:[hierarchical, rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++View CollectionInfo:[hierarchical, rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++View CollectionInfo:[hierarchical, rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++GridView clickable CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="treeGrid", roleDescription="tree grid"]
+++GridView clickable CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="treeGrid", roleDescription="tree grid"]
+++GridView clickable CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="treeGrid", roleDescription="tree grid"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-owns-expected-android-external.txt b/content/test/data/accessibility/aria/aria-owns-expected-android-external.txt
new file mode 100644
index 0000000..0cb7254
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-expected-android-external.txt
@@ -0,0 +1,14 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView CollectionInfo:[rows=3, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="list"]
+++++View CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS] bundle:[chromeRole="listItem"]
+++++++View text:"• " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listMarker"]
+++++++TextView text:"One" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++++View viewIdResName:"two" CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS] bundle:[chromeRole="listItem"]
+++++++View text:"•" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listMarker"]
+++++++TextView text:"Two" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++++View viewIdResName:"four" CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS] bundle:[chromeRole="listItem"]
+++++++View text:"• " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listMarker"]
+++++++TextView text:"Four" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View viewIdResName:"three" CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS] bundle:[chromeRole="listItem"]
+++++View text:"•" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listMarker"]
+++++TextView text:"Three" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-android-external.txt b/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-android-external.txt
new file mode 100644
index 0000000..7db6adb
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-from-display-none-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText viewIdResName:"input-0" clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-owns-ignored-expected-android-external.txt b/content/test/data/accessibility/aria/aria-owns-ignored-expected-android-external.txt
new file mode 100644
index 0000000..58a94bb
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-ignored-expected-android-external.txt
@@ -0,0 +1,14 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"Ordinary presentation element with id" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View text:"An aria-owned element is never ignored" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="group"]
+++++Button text:"button-in-owned-tree" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++View text:"Element with aria-owns is never ignored" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText viewIdResName:"textbox" clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++View text:"Owning an element with an ignored parent serializes cleanly" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="group"]
+++++CheckBox viewIdResName:"checkbox" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="checkBox", clickableScore="300", roleDescription="checkbox"]
+++View text:"All the above in one" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++SeekBar text:"50" viewIdResName:"range" clickable focusable RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, SCROLL_FORWARD, SCROLL_BACKWARD, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-owns-list-expected-android-external.txt b/content/test/data/accessibility/aria/aria-owns-list-expected-android-external.txt
new file mode 100644
index 0000000..86920468
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-owns-list-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView CollectionInfo:[rows=2, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="list"]
+++++View text:"One" viewIdResName:"one" CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listItem"]
+++++View text:"Two" viewIdResName:"two" CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listItem"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-presentation-expected-android-external.txt b/content/test/data/accessibility/aria/aria-presentation-expected-android-external.txt
new file mode 100644
index 0000000..a7eee56
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-presentation-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"aria-role presentation" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-presentation-in-list-expected-android-external.txt b/content/test/data/accessibility/aria/aria-presentation-in-list-expected-android-external.txt
new file mode 100644
index 0000000..cc8b89c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-presentation-in-list-expected-android-external.txt
@@ -0,0 +1,10 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView CollectionInfo:[rows=2, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="list"]
+++++View CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS] bundle:[chromeRole="listItem"]
+++++++View text:"• " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listMarker"]
+++++++TextView text:"One" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS] bundle:[chromeRole="listItem"]
+++++++View text:"• " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listMarker"]
+++++++TextView text:"Two" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++TextView text:"Three" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-pressed-expected-android-external.txt b/content/test/data/accessibility/aria/aria-pressed-expected-android-external.txt
new file mode 100644
index 0000000..2479c55b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-pressed-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++Button text:"Regular button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++ToggleButton text:"Toggle button unpressed, Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button pressed, On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button mixed, Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-progressbar-expected-android-external.txt b/content/test/data/accessibility/aria/aria-progressbar-expected-android-external.txt
new file mode 100644
index 0000000..49390fd1
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-progressbar-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ProgressBar text:"3" RangeInfo:[current=3.0, min=1.0, max=37.0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="progressIndicator", roleDescription="progress indicator"]
+++ProgressBar text:"three" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="progressIndicator", roleDescription="progress indicator"]
+++ProgressBar text:"indeterminate" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="progressIndicator", roleDescription="progress indicator"]
+++ProgressBar text:"indeterminate" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="progressIndicator", roleDescription="progress indicator"]
+++ProgressBar text:"indeterminate, Test label" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="progressIndicator", roleDescription="progress indicator"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-radio-expected-android-external.txt b/content/test/data/accessibility/aria/aria-radio-expected-android-external.txt
new file mode 100644
index 0000000..8ca9400
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-radio-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++RadioButton text:"Radio1" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="radioButton", roleDescription="radio button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-radiogroup-expected-android-external.txt b/content/test/data/accessibility/aria/aria-radiogroup-expected-android-external.txt
new file mode 100644
index 0000000..0525dd7
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-radiogroup-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"My group" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="radioGroup", roleDescription="radio group"]
+++++RadioButton text:"Radio 1" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="radioButton", roleDescription="radio button"]
+++++RadioButton text:"Radio 2" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="radioButton", roleDescription="radio button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-readonly-expected-android-external.txt b/content/test/data/accessibility/aria/aria-readonly-expected-android-external.txt
new file mode 100644
index 0000000..d4b0f622
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-readonly-expected-android-external.txt
@@ -0,0 +1,23 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText text:"Readonly-false input" clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++EditText text:"Readonly-true input" clickable editable disabled focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="textField", clickableScore="300"]
+++TextView text:"Readonly-false plain div" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"Readonly-true plain div" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++EditText clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="genericContainer", clickableScore="200", hint="Readonly-false contenteditable div"]
+++EditText clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="genericContainer", clickableScore="200", hint="Readonly-true contenteditable div"]
+++EditText clickable editable focusable textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Readonly-false role unimplemented textbox"]
+++EditText clickable editable disabled focusable textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="textField", hint="Readonly-true role unimplemented textbox"]
+++EditText clickable editable focusable textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="Readonly-false contenteditable textbox"]
+++EditText clickable editable disabled focusable textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="textField", hint="Readonly-true contenteditable textbox"]
+++CheckBox text:"Readonly checkbox" checkable clickable disabled actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="checkBox", roleDescription="checkbox"]
+++EditText canOpenPopUp clickable editable disabled focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS] bundle:[chromeRole="textFieldWithComboBox", clickableScore="300", hint="Readonly combobox"]
+++ListView text:"Readonly listbox" clickable disabled CollectionInfo:[rows=0, cols=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBox", roleDescription="list box"]
+++View text:"Readonly radiogroup" disabled actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="radioGroup", roleDescription="radio group"]
+++SeekBar text:"50, Readonly slider" clickable disabled RangeInfo:[current=50.0, min=0.0, max=100.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
+++EditText text:"0, Readonly spinbutton" clickable disabled RangeInfo:[current=0.0, min=0.0, max=0.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="spinButton", roleDescription="spin button"]
+++MenuItem text:"Readonly menuitemcheckbox" checkable clickable disabled actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemCheckBox", roleDescription="checkbox"]
+++MenuItem text:"Readonly menuitemradio" checkable clickable disabled actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="menuItemRadio", roleDescription="radio button"]
+++EditText clickable editable disabled textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS] bundle:[chromeRole="searchBox", hint="Readonly searchbox", roleDescription="search text field"]
+++ToggleButton text:"Readonly switch" checkable clickable disabled actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="switch", roleDescription="switch"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-region-expected-android-external.txt b/content/test/data/accessibility/aria/aria-region-expected-android-external.txt
new file mode 100644
index 0000000..cbf1880
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-region-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"Unnamed ARIA region: must fall back to the native role." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++TextView text:"Unnamed ARIA region on plain div uses generic container role." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++View text:"Named region" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
+++View text:"Named ARIA region#2 gets the region role." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
+++++TextView text:"Named ARIA region#2 gets the region role." viewIdResName:"region-name" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++View text:"Named region" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
+++View text:"An aria-rolescription works on a nameless role=region." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="region", roleDescription="region"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-relevant-expected-android-external.txt b/content/test/data/accessibility/aria/aria-relevant-expected-android-external.txt
new file mode 100644
index 0000000..9517a35a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-relevant-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="log", roleDescription="log"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="log", roleDescription="log"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="log", roleDescription="log"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="log", roleDescription="log"]
+++View focusable actions:[FOCUS, AX_FOCUS] bundle:[chromeRole="log", roleDescription="log"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-required-expected-android-external.txt b/content/test/data/accessibility/aria/aria-required-expected-android-external.txt
new file mode 100644
index 0000000..e763ac6
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-required-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="radioGroup", roleDescription="radio group"]
+++++RadioButton checkable clickable actions:[CLICK, AX_FOCUS] bundle:[chromeRole="radioButton", roleDescription="radio button"]
+++++RadioButton checkable clickable actions:[CLICK, AX_FOCUS] bundle:[chromeRole="radioButton", roleDescription="radio button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt b/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
new file mode 100644
index 0000000..fc1ef7e9
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-roledescription-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++Button text:"Native button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++Button text:"ARIA button" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++Button text:"Clicky button" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++TextView text:"foo" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"bar" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++TextView text:"baz" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-row-attr-expected-android-external.txt b/content/test/data/accessibility/aria/aria-row-attr-expected-android-external.txt
new file mode 100644
index 0000000..c6cab40
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-row-attr-expected-android-external.txt
@@ -0,0 +1,9 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=2, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"cell 2" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"cell 3" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"cell 4" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"cell 2" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"cell 3" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-row-expected-android-external.txt b/content/test/data/accessibility/aria/aria-row-expected-android-external.txt
new file mode 100644
index 0000000..0530ac1e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-row-expected-android-external.txt
@@ -0,0 +1,11 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=3, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Chrome" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Blink" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Safari" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"WebKit" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-rowgroup-expected-android-external.txt b/content/test/data/accessibility/aria/aria-rowgroup-expected-android-external.txt
new file mode 100644
index 0000000..fec40ac
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-rowgroup-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="rowGroup"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-rowheader-expected-android-external.txt b/content/test/data/accessibility/aria/aria-rowheader-expected-android-external.txt
new file mode 100644
index 0000000..426d790
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-rowheader-expected-android-external.txt
@@ -0,0 +1,10 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=2, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"Chrome" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Safari" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"Blink" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"WebKit" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-scrollbar-expected-android-external.txt b/content/test/data/accessibility/aria/aria-scrollbar-expected-android-external.txt
new file mode 100644
index 0000000..6307f97
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-scrollbar-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"55" clickable focusable RangeInfo:[current=55.0, min=0.0, max=100.0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="scrollBar", roleDescription="scroll bar"]
+++View text:"55" clickable focusable RangeInfo:[current=55.0, min=0.0, max=100.0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="scrollBar", roleDescription="scroll bar"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-search-expected-android-external.txt b/content/test/data/accessibility/aria/aria-search-expected-android-external.txt
new file mode 100644
index 0000000..66f19414
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-search-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"ARIA role search." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="search", roleDescription="search"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-selected-expected-android-external.txt b/content/test/data/accessibility/aria/aria-selected-expected-android-external.txt
new file mode 100644
index 0000000..904a40e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-selected-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView text:", 2 items" clickable CollectionInfo:[rows=2, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"1, in list, item 1 of 2" clickable focusable selected CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"2, in list, item 2 of 2" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-separator-expected-android-external.txt b/content/test/data/accessibility/aria/aria-separator-expected-android-external.txt
new file mode 100644
index 0000000..e3b9276
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-separator-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"Before" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++View text:"1" clickable focusable RangeInfo:[current=1.0, min=1.0, max=3.0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="splitter", roleDescription="splitter"]
+++TextView text:"After" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-setsize-expected-android-external.txt b/content/test/data/accessibility/aria/aria-setsize-expected-android-external.txt
new file mode 100644
index 0000000..b8159d8
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-setsize-expected-android-external.txt
@@ -0,0 +1,12 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView text:", 4 items" clickable CollectionInfo:[rows=4, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Item 1, in list, item 1 of 4" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 2, in list, item 2 of 4" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 3, in list, item 3 of 4" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 4, in list, item 4 of 4" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++ListView text:", 5 items" clickable CollectionInfo:[rows=5, cols=0] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="listBox", clickableScore="300", roleDescription="list box"]
+++++View text:"Item 1, in list, item 1 of 5" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 2, in list, item 2 of 5" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 3, in list, item 3 of 5" clickable focusable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 4, in list, item 4 of 5" clickable focusable CollectionItemInfo:[rowIndex=3, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
+++++View text:"Item 5, in list, item 5 of 5" clickable focusable CollectionItemInfo:[rowIndex=4, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="listBoxOption", clickableScore="200"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-slider-expected-android-external.txt b/content/test/data/accessibility/aria/aria-slider-expected-android-external.txt
new file mode 100644
index 0000000..742c4b4
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-slider-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++SeekBar text:"5" clickable RangeInfo:[current=5.0, min=1.0, max=10.0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, SET_PROGRESS] bundle:[chromeRole="slider", roleDescription="slider"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-sort-aria-grid-expected-android-external.txt b/content/test/data/accessibility/aria/aria-sort-aria-grid-expected-android-external.txt
new file mode 100644
index 0000000..68686b4
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-sort-aria-grid-expected-android-external.txt
@@ -0,0 +1,61 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS] bundle:[chromeRole="grid", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-sort-html-table-expected-android-external.txt b/content/test/data/accessibility/aria/aria-sort-html-table-expected-android-external.txt
new file mode 100644
index 0000000..7f18e2c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-sort-html-table-expected-android-external.txt
@@ -0,0 +1,69 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView text:"Data table" CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=1, cols=3] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="rowHeader", roleDescription="row header"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=2, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView text:"Data table" CollectionInfo:[rows=3, cols=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Alphabet" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"A" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"B" CollectionItemInfo:[rowIndex=2, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++TextView text:"Alphabet" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-status-expected-android-external.txt b/content/test/data/accessibility/aria/aria-status-expected-android-external.txt
new file mode 100644
index 0000000..439c4d2
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-status-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="status", roleDescription="status"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="status", roleDescription="status"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-strong-expected-android-external.txt b/content/test/data/accessibility/aria/aria-strong-expected-android-external.txt
new file mode 100644
index 0000000..33b5fee
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-strong-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"role" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="strong"]
+++TextView text:"element (no name)" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"include me" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="strong"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-subscript-expected-android-external.txt b/content/test/data/accessibility/aria/aria-subscript-expected-android-external.txt
new file mode 100644
index 0000000..7d02eee
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-subscript-expected-android-external.txt
@@ -0,0 +1,17 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="paragraph"]
+++++TextView text:"This text contains " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"subscript" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="subscript"]
+++++TextView text:" text." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="paragraph"]
+++++TextView text:"H" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="subscript"]
+++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="contentDeletion", roleDescription="deletion"]
+++++++++View text:"3" focusable actions:[FOCUS, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="contentInsertion", roleDescription="insertion"]
+++++++++View text:"null" contentDescription:"2" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-subscript.html#"]
+++++++++++TextView text:"better" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", clickableScore="100"]
+++++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:"O" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-superscript-expected-android-external.txt b/content/test/data/accessibility/aria/aria-superscript-expected-android-external.txt
new file mode 100644
index 0000000..08ed889
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-superscript-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="paragraph"]
+++++TextView text:"This text contains " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"superscript" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="superscript"]
+++++TextView text:" text." actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-switch-expected-android-external.txt b/content/test/data/accessibility/aria/aria-switch-expected-android-external.txt
new file mode 100644
index 0000000..e403fed
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-switch-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ToggleButton text:"Switch1" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="switch", roleDescription="switch"]
+++ToggleButton text:"Switch2" checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="switch", roleDescription="switch"]
+++ToggleButton text:"Switch3" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="switch", roleDescription="switch"]
+++ToggleButton text:"Switch4" checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="switch", roleDescription="switch"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tab-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tab-expected-android-external.txt
new file mode 100644
index 0000000..ae765fc
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tab-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"Tab 1" viewIdResName:"tab1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"Tab 2" viewIdResName:"tab2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tab-nested-in-lists-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tab-nested-in-lists-expected-android-external.txt
new file mode 100644
index 0000000..0f5b376
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tab-nested-in-lists-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"tab1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"tab2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"tab3" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"tab4" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-table-expected-android-external.txt b/content/test/data/accessibility/aria/aria-table-expected-android-external.txt
new file mode 100644
index 0000000..ef0a92c5
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-table-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView CollectionInfo:[rows=2, cols=2] actions:[AX_FOCUS] bundle:[chromeRole="table", roleDescription="table"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Browser" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++++View text:"Rendering Engine" CollectionItemInfo:[heading, rowIndex=0, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="columnHeader", roleDescription="column header"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Chrome" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++++View text:"Blink" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=1, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tablist-aria-level-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tablist-aria-level-expected-android-external.txt
new file mode 100644
index 0000000..040eb8ac
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tablist-aria-level-expected-android-external.txt
@@ -0,0 +1,14 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"Tab 1 of 2, level 1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"Tab 2 of 2, level 1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"Tab 1 of 3, level 2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"Tab 2 of 3, level 2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++++View text:"Tab 1 of 1, level 3" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"Tab 3 of 3, level 2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"Tab 1 of 1, level 2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"Tab 1 of 1, level 1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tablist-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tablist-expected-android-external.txt
new file mode 100644
index 0000000..fc5fa3f8
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tablist-expected-android-external.txt
@@ -0,0 +1,11 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++View text:"Tab 1, level 1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"Tab 2, level 1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++++View text:"Tab 1, level 2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++++View text:"Tab 2, level 2" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++++++TextView text:"Tab 2, level 2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++++TabWidget actions:[AX_FOCUS] bundle:[chromeRole="tabList", roleDescription="tab list"]
+++++++++++View text:"Tab 1, level 3" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++View text:"Tab 3, level 1" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tabpanel-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tabpanel-expected-android-external.txt
new file mode 100644
index 0000000..c0adf9ad
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tabpanel-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="tabPanel", roleDescription="tab panel"]
+++++View text:"Item" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++++TextView text:"Item" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 3"]
+++++View text:"Prices" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tab", roleDescription="tab"]
+++++++TextView text:"Prices" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 3"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-term-expected-android-external.txt b/content/test/data/accessibility/aria/aria-term-expected-android-external.txt
new file mode 100644
index 0000000..2cb302c
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-term-expected-android-external.txt
@@ -0,0 +1,6 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ListView CollectionInfo:[rows=0, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="list"]
+++++View text:"Term1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="term", roleDescription="term"]
+++++View text:"Definition1" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="definition", roleDescription="definition"]
+++++View text:"Term2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="term", roleDescription="term"]
+++++View text:"Definition2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="definition", roleDescription="definition"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-textbox-expected-android-external.txt b/content/test/data/accessibility/aria/aria-textbox-expected-android-external.txt
new file mode 100644
index 0000000..0252af40
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-textbox-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText text:"\n    TextBox1\n  " clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
+++EditText text:"\n    TextBox2\n  " clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-textbox-with-aria-textbox-child-expected-android-external.txt b/content/test/data/accessibility/aria/aria-textbox-with-aria-textbox-child-expected-android-external.txt
new file mode 100644
index 0000000..d1b566e
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-textbox-with-aria-textbox-child-expected-android-external.txt
@@ -0,0 +1,9 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText text:"Foo\n\nBar\n\nBar\n\nBaz\n\nBaz" clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="not editable"]
+++++TextView text:"Foo" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++++EditText text:"Bar" clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="nested, not editable, rich"]
+++++++TextView text:"Bar" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="paragraph"]
+++++EditText text:"Bar" clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="nested, not editable, plain"]
+++++EditText text:"Baz" clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="nested, editable, rich"]
+++++EditText text:"Baz" clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="nested, editable, plain"]
+++EditText text:"Foo\n\nBar\n\nBar\n\nBaz\n\nBaz" clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="editable"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-textbox-with-non-text-children-expected-android-external.txt b/content/test/data/accessibility/aria/aria-textbox-with-non-text-children-expected-android-external.txt
new file mode 100644
index 0000000..d839cce
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-textbox-with-non-text-children-expected-android-external.txt
@@ -0,0 +1,30 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea", hasImage="true"]
+++EditText text:"focusable: ok link name: " clickable editable focusable textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="not editable"]
+++++TextView text:"focusable: " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++Button text:"ok" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++View text:"null" contentDescription:"link" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-textbox-with-non-text-children.html#"]
+++++++TextView text:"link" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="labelText"]
+++++++TextView text:"name: " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="name:"]
+++EditText text:"focusable: ok link name: " clickable editable focusable multiLine textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="editable"]
+++++TextView text:"focusable: " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++Button text:"ok" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++View text:"null" contentDescription:"link" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-textbox-with-non-text-children.html#"]
+++++++TextView text:"link" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="labelText", clickableScore="100"]
+++++++TextView text:"name: " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="name:"]
+++EditText text:"focusable: ok link name: " clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COPY, PASTE, CUT, SET_SELECTION, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hint="not editable or focusable"]
+++++TextView text:"focusable: " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++Button text:"ok" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", clickableScore="300", roleDescription="button"]
+++++View text:"null" contentDescription:"link" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-textbox-with-non-text-children.html#"]
+++++++TextView text:"link" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="labelText"]
+++++++TextView text:"name: " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="name:"]
+++EditText clickable editable textSelectionStart:0 textSelectionEnd:0 actions:[CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", hasImage="true", hint="not editable, img child"]
+++++Image actions:[AX_FOCUS] bundle:[chromeRole="image", hasImage="true", roleDescription="graphic"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-time-expected-android-external.txt b/content/test/data/accessibility/aria/aria-time-expected-android-external.txt
new file mode 100644
index 0000000..9e91fe6
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-time-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"role" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="time"]
+++View text:"element (no name)" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="time"]
+++TextView text:" " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++View text:"include me" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="time"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-timer-expected-android-external.txt b/content/test/data/accessibility/aria/aria-timer-expected-android-external.txt
new file mode 100644
index 0000000..4812f18
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-timer-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"This test is for aria-role = timer" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="timer", roleDescription="timer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-togglebutton-expected-android-external.txt b/content/test/data/accessibility/aria/aria-togglebutton-expected-android-external.txt
new file mode 100644
index 0000000..97cd51d
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-togglebutton-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++Button text:"Regular button" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="button", roleDescription="button"]
+++ToggleButton text:"Toggle button, Off" checkable clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, On" checkable checked clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, Partially Checked" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-toolbar-expected-android-external.txt b/content/test/data/accessibility/aria/aria-toolbar-expected-android-external.txt
new file mode 100644
index 0000000..cffeca7
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-toolbar-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"A toolbar" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="toolbar", roleDescription="toolbar"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tooltip-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tooltip-expected-android-external.txt
new file mode 100644
index 0000000..d263369
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tooltip-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText viewIdResName:"username" clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="Your username should be your email id"]
+++View text:"Your username should be your email id" viewIdResName:"username-tip" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="tooltip", roleDescription="tooltip"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-android-external.txt
new file mode 100644
index 0000000..ab5cda05
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-discontinuous-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View CollectionInfo:[hierarchical, rows=2, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++++View text:"card content" clickable focusable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++TextView actions:[AX_FOCUS] bundle:[chromeRole="genericContainer"]
+++++View text:"card content" clickable focusable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-tree-expected-android-external.txt b/content/test/data/accessibility/aria/aria-tree-expected-android-external.txt
new file mode 100644
index 0000000..7e74163a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-tree-expected-android-external.txt
@@ -0,0 +1,22 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View CollectionInfo:[hierarchical, rows=2, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++++View text:"Animals, Partially Checked" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++++View text:"null" contentDescription:"Animals" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-tree.html#animals"]
+++++++++TextView text:"Animals" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="group"]
+++++++++View text:"Domesticated" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++++++++View text:"null" contentDescription:"Domesticated" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-tree.html#domesticated"]
+++++++++++++TextView text:"Domesticated" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++++++View actions:[AX_FOCUS] bundle:[chromeRole="group"]
+++++++++++++View text:"Dog" checkable checked clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++++++++++++View text:"null" contentDescription:"Dog" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-tree.html#dog"]
+++++++++++++++++TextView text:"Dog" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++++++++View text:"Cat" checkable clickable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++++++++++++View text:"null" contentDescription:"Cat" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-tree.html#cat"]
+++++++++++++++++TextView text:"Cat" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++++View text:"Wild" clickable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++++++++View text:"null" contentDescription:"Wild" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-tree.html#wild"]
+++++++++++++TextView text:"Wild" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"Plants" clickable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++++View text:"null" contentDescription:"Plants" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/aria-tree.html#plants"]
+++++++++TextView text:"Plants" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-treegrid-expected-android-external.txt b/content/test/data/accessibility/aria/aria-treegrid-expected-android-external.txt
new file mode 100644
index 0000000..fd66bef8b
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-treegrid-expected-android-external.txt
@@ -0,0 +1,10 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++GridView clickable CollectionInfo:[rows=2, cols=1] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="treeGrid", roleDescription="tree grid"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Cell at level 1" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++View text:"Cell at level 2" CollectionItemInfo:[rowIndex=1, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
+++GridView clickable CollectionInfo:[rows=1, cols=1] actions:[CLICK, AX_FOCUS] bundle:[chromeRole="treeGrid", roleDescription="tree grid"]
+++++View actions:[AX_FOCUS] bundle:[chromeRole="rowGroup"]
+++++++View actions:[AX_FOCUS] bundle:[chromeRole="row"]
+++++++++View text:"Cell at level 1" CollectionItemInfo:[rowIndex=0, rowSpan=1, colIndex=0, colSpan=1] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="cell"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android-external.txt b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android-external.txt
new file mode 100644
index 0000000..191518ab
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-treeitem-nested-in-lists-expected-android-external.txt
@@ -0,0 +1,7 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View CollectionInfo:[hierarchical, rows=5, cols=0] actions:[AX_FOCUS] bundle:[chromeRole="tree", roleDescription="tree"]
+++++View text:"treeitem 2 of 5, level 1" clickable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++View text:"treeitem 3 of 5, level 1" clickable CollectionItemInfo:[rowIndex=2, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++View text:"treeitem 1 of 2, level 2" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++View text:"treeitem 1 of 1, level 3" clickable CollectionItemInfo:[rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
+++++View text:"treeitem 2 of 2, level 2" clickable CollectionItemInfo:[rowIndex=1, rowSpan=0, colIndex=0, colSpan=0] actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="treeItem", roleDescription="tree item"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-valuenow-expected-android-external.txt b/content/test/data/accessibility/aria/aria-valuenow-expected-android-external.txt
new file mode 100644
index 0000000..810d90a
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-valuenow-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ProgressBar text:"3" RangeInfo:[current=3.0, min=0.0, max=100.0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="progressIndicator", roleDescription="progress indicator"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-valuetext-expected-android-external.txt b/content/test/data/accessibility/aria/aria-valuetext-expected-android-external.txt
new file mode 100644
index 0000000..7c937ab
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-valuetext-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ProgressBar text:"three" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="progressIndicator", roleDescription="progress indicator"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/aria-virtualcontent-expected-android-external.txt b/content/test/data/accessibility/aria/aria-virtualcontent-expected-android-external.txt
new file mode 100644
index 0000000..29562520
--- /dev/null
+++ b/content/test/data/accessibility/aria/aria-virtualcontent-expected-android-external.txt
@@ -0,0 +1,2 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"hello world" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/dpub-roles-expected-android-external.txt b/content/test/data/accessibility/aria/dpub-roles-expected-android-external.txt
new file mode 100644
index 0000000..ccc4a8ea
--- /dev/null
+++ b/content/test/data/accessibility/aria/dpub-roles-expected-android-external.txt
@@ -0,0 +1,42 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea", hasImage="true"]
+++View text:"doc-abstract" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docAbstract", roleDescription="abstract"]
+++View text:"doc-acknowledgments" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docAcknowledgments", roleDescription="acknowledgments"]
+++View text:"doc-afterword" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docAfterword", roleDescription="afterword"]
+++View text:"doc-appendix" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docAppendix", roleDescription="appendix"]
+++View text:"null" contentDescription:"doc-backlink" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docBackLink", roleDescription="back link"]
+++View text:"doc-biblioentry" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docBiblioEntry", roleDescription="bibliography entry"]
+++View text:"doc-bibliography" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docBibliography", roleDescription="bibliography"]
+++View text:"null" contentDescription:"doc-biblioref" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docBiblioRef", roleDescription="bibliography reference"]
+++View text:"doc-chapter" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docChapter", roleDescription="chapter"]
+++View text:"doc-colophon" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docColophon", roleDescription="colophon"]
+++View text:"doc-conclusion" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docConclusion", roleDescription="conclusion"]
+++View text:"doc-cover" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docCover", hasImage="true", roleDescription="cover"]
+++View text:"doc-credit" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docCredit", roleDescription="credit"]
+++View text:"doc-credits" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docCredits", roleDescription="credits"]
+++View text:"doc-dedication" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docDedication", roleDescription="dedication"]
+++View text:"doc-endnote" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docEndnote", roleDescription="endnote"]
+++View text:"doc-endnotes" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docEndnotes", roleDescription="endnotes"]
+++View text:"doc-epigraph" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docEpigraph", roleDescription="epigraph"]
+++View text:"doc-epilogue" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docEpilogue", roleDescription="epilogue"]
+++View text:"doc-errata" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docErrata", roleDescription="errata"]
+++View text:"doc-example" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docExample", roleDescription="example"]
+++View text:"doc-footnote" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docFootnote", roleDescription="footnote"]
+++View text:"doc-foreword" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docForeword", roleDescription="foreword"]
+++View text:"doc-glossary" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docGlossary", roleDescription="glossary"]
+++View text:"null" contentDescription:"doc-glossref" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docGlossref", roleDescription="glossary reference"]
+++View text:"doc-index" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docIndex", roleDescription="index"]
+++View text:"doc-introduction" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docIntroduction", roleDescription="introduction"]
+++View text:"null" contentDescription:"doc-noteref" clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docNoteRef", roleDescription="note reference"]
+++View text:"doc-notice" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docNotice", roleDescription="notice"]
+++View text:"doc-pagebreak" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPageBreak", roleDescription="page break"]
+++View text:"doc-pagefooter" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPageFooter", roleDescription="page footer"]
+++View text:"doc-pageheader" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPageHeader", roleDescription="page header"]
+++View text:"doc-pagelist" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPageList", roleDescription="page list"]
+++View text:"doc-part" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPart", roleDescription="part"]
+++View text:"doc-preface" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPreface", roleDescription="preface"]
+++View text:"doc-prologue" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPrologue", roleDescription="prologue"]
+++View text:"doc-pullquote" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docPullquote", roleDescription="pullquote"]
+++View text:"doc-qna" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docQna", roleDescription="Q&A"]
+++View text:"doc-subtitle" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docSubtitle", roleDescription="subtitle"]
+++View text:"doc-tip" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docTip", roleDescription="tip"]
+++View text:"doc-toc" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="docToc", roleDescription="table of contents"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/graphics-roles-expected-android-external.txt b/content/test/data/accessibility/aria/graphics-roles-expected-android-external.txt
new file mode 100644
index 0000000..d314c82
--- /dev/null
+++ b/content/test/data/accessibility/aria/graphics-roles-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea", hasImage="true"]
+++View text:"graphics-document" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="graphicsDocument", roleDescription="graphics document"]
+++View text:"graphics-object" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="graphicsObject", roleDescription="graphics object"]
+++View text:"graphics-symbol" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="graphicsSymbol", hasImage="true", roleDescription="graphics symbol"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/hidden-described-by-expected-android-external.txt b/content/test/data/accessibility/aria/hidden-described-by-expected-android-external.txt
new file mode 100644
index 0000000..dec360a
--- /dev/null
+++ b/content/test/data/accessibility/aria/hidden-described-by-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView text:"span-A3" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="span-A4"]
+++TextView text:"span-B" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer", hint="span-A2"]
+++TextView text:"span-C" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/hidden-expected-android-external.txt b/content/test/data/accessibility/aria/hidden-expected-android-external.txt
new file mode 100644
index 0000000..04c7db0
--- /dev/null
+++ b/content/test/data/accessibility/aria/hidden-expected-android-external.txt
@@ -0,0 +1,3 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++TextView actions:[AX_FOCUS] bundle:[chromeRole="genericContainer", hint="b"]
+++TextView actions:[AX_FOCUS] bundle:[chromeRole="genericContainer", hint="c"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/hidden-labelled-by-expected-android-external.txt b/content/test/data/accessibility/aria/hidden-labelled-by-expected-android-external.txt
new file mode 100644
index 0000000..543de59
--- /dev/null
+++ b/content/test/data/accessibility/aria/hidden-labelled-by-expected-android-external.txt
@@ -0,0 +1,4 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View text:"span-A4" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++View text:"span-A2" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="group"]
+++TextView text:"span-C" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/input-text-aria-placeholder-expected-android-external.txt b/content/test/data/accessibility/aria/input-text-aria-placeholder-expected-android-external.txt
new file mode 100644
index 0000000..06a8df6
--- /dev/null
+++ b/content/test/data/accessibility/aria/input-text-aria-placeholder-expected-android-external.txt
@@ -0,0 +1,8 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="aria-placeholder1"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="placeholder2"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="aria-label3 placeholder3"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="aria-label4 aria-placeholder4"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="aria-label5 placeholder5 aria-description5"]
+++TextView text:"aria-description5" viewIdResName:"ariadesc5" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="genericContainer"]
+++EditText clickable editable focusable inputType:1 textSelectionStart:0 textSelectionEnd:0 actions:[FOCUS, CLICK, AX_FOCUS, PASTE, SET_TEXT, IME_ENTER] bundle:[chromeRole="textField", clickableScore="300", hint="aria-placeholder6 title6"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/presentational-expected-android-external.txt b/content/test/data/accessibility/aria/presentational-expected-android-external.txt
new file mode 100644
index 0000000..f61444c
--- /dev/null
+++ b/content/test/data/accessibility/aria/presentational-expected-android-external.txt
@@ -0,0 +1,9 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++View actions:[AX_FOCUS] bundle:[chromeRole="section"]
+++++View text:"Heading with link" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="heading", roleDescription="heading 2"]
+++++++TextView text:"Heading " CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++++View text:"null" contentDescription:"with link" clickable focusable CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/presentational.html#"]
+++++++++TextView text:"with link" CollectionItemInfo:[heading, rowIndex=0, rowSpan=0, colIndex=0, colSpan=0] actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++TextView text:"Presentational heading " actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
+++++View text:"null" contentDescription:"with link" clickable focusable actions:[FOCUS, CLICK, AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="link", clickableScore="300", roleDescription="link", targetUrl="file:///storage/emulated/0/chromium_tests_root/content/test/data/accessibility/aria/presentational.html#"]
+++++++TextView text:"with link" actions:[AX_FOCUS, NEXT, PREVIOUS] bundle:[chromeRole="staticText"]
\ No newline at end of file
diff --git a/content/test/data/accessibility/aria/toggle-button-expand-collapse-expected-android-external.txt b/content/test/data/accessibility/aria/toggle-button-expand-collapse-expected-android-external.txt
new file mode 100644
index 0000000..48ab484
--- /dev/null
+++ b/content/test/data/accessibility/aria/toggle-button-expand-collapse-expected-android-external.txt
@@ -0,0 +1,5 @@
+WebView focusable focused scrollable actions:[CLEAR_FOCUS, AX_FOCUS] bundle:[chromeRole="rootWebArea"]
+++ToggleButton text:"Toggle button, pressed, collapsed, On" canOpenPopUp checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, not pressed, collapsed, Off" canOpenPopUp checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, EXPAND] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, pressed, expanded, On" canOpenPopUp checkable checked clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
+++ToggleButton text:"Toggle button, not pressed, collapsed", Off" canOpenPopUp checkable clickable actions:[CLICK, AX_FOCUS, NEXT, PREVIOUS, COLLAPSE] bundle:[chromeRole="toggleButton", roleDescription="toggle button"]
\ No newline at end of file
diff --git a/device/fido/cable/cable_discovery_data.cc b/device/fido/cable/cable_discovery_data.cc
index d629c94..4355d68 100644
--- a/device/fido/cable/cable_discovery_data.cc
+++ b/device/fido/cable/cable_discovery_data.cc
@@ -19,6 +19,8 @@
 #include "third_party/boringssl/src/include/openssl/hkdf.h"
 #include "third_party/boringssl/src/include/openssl/mem.h"
 #include "third_party/boringssl/src/include/openssl/obj.h"
+#include "third_party/icu/source/common/unicode/locid.h"
+#include "third_party/icu/source/i18n/unicode/coll.h"
 
 namespace device {
 
diff --git a/device/fido/cable/cable_discovery_data.h b/device/fido/cable/cable_discovery_data.h
index 248de04..148e92b 100644
--- a/device/fido/cable/cable_discovery_data.h
+++ b/device/fido/cable/cable_discovery_data.h
@@ -6,7 +6,11 @@
 #define DEVICE_FIDO_CABLE_CABLE_DISCOVERY_DATA_H_
 
 #include <stdint.h>
+
 #include <array>
+#include <memory>
+#include <string>
+#include <vector>
 
 #include "base/component_export.h"
 #include "base/containers/span.h"
@@ -15,13 +19,18 @@
 #include "device/fido/fido_constants.h"
 #include "third_party/abseil-cpp/absl/types/optional.h"
 #include "third_party/abseil-cpp/absl/types/variant.h"
-#include "third_party/icu/source/common/unicode/locid.h"
-#include "third_party/icu/source/i18n/unicode/coll.h"
+#include "third_party/icu/source/common/unicode/uversion.h"
 
 namespace cbor {
 class Value;
 }
 
+// third_party/icu/source/common/unicode/uversion.h will set namespace icu.
+namespace U_ICU_NAMESPACE {
+class Collator;
+class Locale;
+}  // namespace U_ICU_NAMESPACE
+
 namespace device {
 
 constexpr size_t kCableEphemeralIdSize = 16;
diff --git a/infra/config/lib/args.star b/infra/config/lib/args.star
index bb8423ae..11fbd40f 100644
--- a/infra/config/lib/args.star
+++ b/infra/config/lib/args.star
@@ -15,26 +15,42 @@
 # between an unspecified value and an explicit value of None
 DEFAULT = _sentinel("__default__")
 
-def defaults(extends = None, **vars):
-    """Define a structure that provides module-level defaults for function
-    arguments.
+# A sentinel value that can be passed as the merge argument to
+# get_value/get_value_from_kwargs to have the provided value merged as a list.
+MERGE_LIST = _sentinel("__merge_list__")
 
-    Arguments:
-      * extends - A struct containing `lucicfg.var` attributes to add to the
+# A sentinel value that can be passed as the merge argument to
+# get_value/get_value_from_kwargs to have the provided value merged as a dict.
+MERGE_DICT = _sentinel("__merge_dict__")
+
+_IGNORE_DEFAULT_ATTR = "_ignore_default"
+
+def defaults(extends = None, **vars):
+    """Define a structure provides a group of module-level defaults.
+
+    Args:
+      extends: A struct containing `lucicfg.var` attributes to add to the
         resulting struct.
-      * vars - Defaults to define. Each entry results in a `lucicfg.var` attribute
+      **vars: Defaults to define. Each entry results in a `lucicfg.var` attribute
         being added to the resulting struct. The name of the attribute is the
         keyword and the default value of the `lucicfg.var` is the keyword's value.
 
     Returns:
       A struct containing `lucicfg.var` attributes providing the module level
       defaults and the following methods:
-      * get_value(name, value) - Gets the value of an argument. If `value` is not
-        `DEFAULT`, `value` is returned. Otherwise, the module-level default for
-        `name` is returned.
-      * get_value_from_kwargs(name, kwargs) - Gets the value of a keyword
-        argument. If `name` is in `kwargs`, `kwargs[name]` is returned. Otherwise,
-        the module-level default for `name` is returned.
+      * get_value(name, value, merge=None) - Gets the value of an argument. The
+        behavior of the function depends on the value of the `merge` argument:
+        * None (default) - If `value` is not `DEFAULT`, `value` is returned.
+          Otherwise, the module-level default for `name` is returned.
+        * MERGE_LIST - If `value` is wrapped using `ignore_defaults`, the result
+          of calling `listify(value.value)` is returned. Otherwise, the result
+          of calling `listify` with the module-level default for `name` and
+          `value` is returned.
+        * MERGE_DICT - If `value` is wrapped using `ignore_defaults`,
+          `value.value` is returned. Otherwise, the returned value will be the
+          module-level default for `name` updated with `value`.
+      * get_value_from_kwargs(name, kwargs, merge=None) - Gets the value of a keyword
+        argument.
       * set(**kwargs) - Sets module-level defaults. For each keyword, sets the
         module-level default with the keyword as the name to the value of the
         keyword.
@@ -50,13 +66,38 @@
         if a not in methods:
             vars[a] = getattr(extends, a)
 
-    def get_value(name, value):
-        if value != DEFAULT:
-            return value
-        return vars[name].get()
+    def get_value(name, value, merge = None):
+        default = vars[name].get()
+        ignore_default, value = _should_ignore_default(value)
 
-    def get_value_from_kwargs(name, kwargs):
-        return get_value(name, kwargs.get(name, DEFAULT))
+        if not merge:
+            if ignore_default:
+                fail("attribute {!r} does not merge with the default, ignore_defaults cannot be used".format(name))
+            if value != DEFAULT:
+                return value
+            return default
+
+        if merge == MERGE_DICT:
+            if value == DEFAULT:
+                value = {}
+            if value and type(value) != type({}):
+                fail("attribute {!r} requires a dict value or None, got {!r}".format(name, value))
+            value = value or {}
+            if ignore_default:
+                return value
+            new_value = default or {}
+            new_value.update(value)
+            return new_value
+
+        if merge == MERGE_LIST:
+            if value == DEFAULT:
+                value = []
+            if ignore_default:
+                return listify(value)
+            return listify(default, value)
+
+    def get_value_from_kwargs(name, kwargs, merge = None):
+        return get_value(name, kwargs.get(name, DEFAULT), merge = merge)
 
     def set(**kwargs):
         for k, v in kwargs.items():
@@ -69,8 +110,59 @@
         **vars
     )
 
+def ignore_default(value):
+    """Wraps a value to ignore defaults for the argument.
+
+    For arguments that merge the provided value with a module-level default,
+    this provides a way to explicitly set an exact value. It is an error to use
+    this for attributes that don't merge with the module-level default.
+    """
+    return struct(
+        _ignore_default = True,
+        value = value,
+    )
+
+def _should_ignore_default(value):
+    ignore_default = getattr(value, _IGNORE_DEFAULT_ATTR, False)
+    if ignore_default:
+        value = value.value
+    return ignore_default, value
+
+def listify(*args):
+    """Create a single list from multiple arguments.
+
+    Each argument can be either a single element or a list of elements. A single
+    element will appear as an element in the resulting list iff it is non-None.
+    A list of elements will have all non-None elements appear in the resulting
+    list.
+
+    Args:
+      *args: The arguments to merge into a list.
+
+    Returns:
+      A list composed of the pased in arguments.
+    """
+    wrap_in_ignore_default = False
+    l = []
+    for a in args:
+        if type(a) != type([]):
+            a = [a]
+        for e in a:
+            should_ignore_default, val = _should_ignore_default(e)
+            if should_ignore_default:
+                wrap_in_ignore_default = True
+            if val != None:
+                l.append(val)
+    if wrap_in_ignore_default:
+        return ignore_default(l)
+    return l
+
 args = struct(
     COMPUTE = COMPUTE,
     DEFAULT = DEFAULT,
+    MERGE_LIST = MERGE_LIST,
+    MERGE_DICT = MERGE_DICT,
     defaults = defaults,
+    ignore_default = ignore_default,
+    listify = listify,
 )
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index 1d68306..debecce 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -30,7 +30,6 @@
 load("./branches.star", "branches")
 load("./bootstrap.star", "register_bootstrap")
 load("./builder_config.star", "builder_config", "register_builder_config")
-load("./listify.star", "listify")
 load("./recipe_experiments.star", "register_recipe_experiments_ref")
 
 ################################################################################
@@ -368,6 +367,7 @@
     # unnecessarily make wrapper functions
     bucket = args.COMPUTE,
     executable = args.COMPUTE,
+    notifies = None,
     triggered_by = args.COMPUTE,
 )
 
@@ -377,6 +377,7 @@
         branch_selector = branches.MAIN,
         bucket = args.DEFAULT,
         executable = args.DEFAULT,
+        notifies = None,
         triggered_by = args.DEFAULT,
         os = args.DEFAULT,
         builderless = args.DEFAULT,
@@ -438,6 +439,9 @@
             (may be specified by module-level default).
         executable: an executable to run, e.g. a luci.recipe(...). Required (may
             be specified by module-level default).
+        notifies: A string or list of strings with notifiers that will be
+            triggered for builds of the builder. Supports a module-level default
+            that will be merged with the provided values.
         triggered_by: an optional poller or builder that triggers the builder or
             a list of pollers and/or builders that trigger the builder. Supports
             a module-level default.
@@ -644,7 +648,7 @@
     if pool:
         dimensions["pool"] = pool
 
-    sheriff_rotations = listify(defaults.sheriff_rotations.get(), sheriff_rotations)
+    sheriff_rotations = defaults.get_value("sheriff_rotations", sheriff_rotations, merge = args.MERGE_LIST)
     if sheriff_rotations:
         properties["sheriff_rotations"] = sheriff_rotations
 
@@ -732,6 +736,8 @@
             fail("triggered testers cannot specify triggered_by")
         triggered_by = [builder_spec.parent]
 
+    kwargs["notifies"] = defaults.get_value("notifies", notifies, merge = args.MERGE_LIST)
+
     triggered_by = defaults.get_value("triggered_by", triggered_by)
     if triggered_by != args.COMPUTE:
         kwargs["triggered_by"] = triggered_by
diff --git a/infra/config/lib/ci.star b/infra/config/lib/ci.star
index 8dafdb708..caa996c2 100644
--- a/infra/config/lib/ci.star
+++ b/infra/config/lib/ci.star
@@ -16,12 +16,15 @@
 load("./args.star", "args")
 load("./branches.star", "branches")
 load("./builders.star", "builders", "os", "os_category")
-load("./listify.star", "listify")
+load("//project.star", "settings")
 
 defaults = args.defaults(
     extends = builders.defaults,
     main_console_view = None,
     cq_mirrors_console_view = None,
+    thin_tester_cores = args.DEFAULT,
+    tree_closing = False,
+    tree_closing_notifiers = None,
 )
 
 def ci_builder(
@@ -33,50 +36,60 @@
         main_console_view = args.DEFAULT,
         cq_mirrors_console_view = args.DEFAULT,
         sheriff_rotations = None,
-        tree_closing = False,
+        tree_closing = args.DEFAULT,
+        tree_closing_notifiers = None,
         notifies = None,
         resultdb_bigquery_exports = None,
         experiments = None,
         **kwargs):
     """Define a CI builder.
 
-    Arguments:
-      name - name of the builder, will show up in UIs and logs. Required.
-      branch_selector - A branch selector value controlling whether the
+    Args:
+      name: name of the builder, will show up in UIs and logs. Required.
+      branch_selector: A branch selector value controlling whether the
         builder definition is executed. See branches.star for more
         information.
-      bootstrap - a boolean indicating whether the builder should have its
+      bootstrap: a boolean indicating whether the builder should have its
         properties bootstrapped. If True, the builder's properties will be
         written to a separate file and its definition will be updated with
         new properties and executable that cause a bootstrapping binary to
         be used. The build's default values for properties will be taken
         from the properties file at the version that the build will check
         out.
-      console_view_entry - A `consoles.console_view_entry` struct or a list of
+      console_view_entry: A `consoles.console_view_entry` struct or a list of
         them describing console view entries to create for the builder.
         See `consoles.console_view_entry` for details.
-      main_console_view - A string identifying the ID of the main console
+      main_console_view: A string identifying the ID of the main console
         view to add an entry to. Supports a module-level default that
         defaults to None. An entry will be added only if
         `console_view_entry` is provided and the first entry's branch
-        selector causes the entry to be defined.
-      cq_mirrors_console_view - A string identifying the ID of the CQ
+        selector causes the entry to be defined. On branches, the provided
+        value is ignored; all CI builders will be added to the `main` console.
+      cq_mirrors_console_view: A string identifying the ID of the CQ
         mirrors console view to add an entry to. Supports a module-level
         default that defaults to None. An entry will be added only if
         `console_view_entry` is provided and the first entry's branch
         selector causes the entry to be defined.
-      tree_closing - If true, failed builds from this builder that meet certain
+      sheriff_rotations: The name(s) of any sheriff rotations that the builder
+        should be added to. On branches, all CI builders will be added to the
+        `chrome_browser_release` sheriff rotation.
+      tree_closing: If true, failed builds from this builder that meet certain
         criteria will close the tree and email the sheriff. See the
         'chromium-tree-closer' config in notifiers.star for the full criteria.
-      notifies - Any extra notifiers to attach to this builder.
-      resultdb_bigquery_exports - a list of resultdb.export_test_results(...)
+      tree_closing_notifiers: A list of notifiers that will be notified when
+        tree closing criteria are met by a build of this builder. Supports a
+        module-level default that will be merged with the provided value.
+      notifies: Any extra notifiers to attach to this builder.
+      resultdb_bigquery_exports: a list of resultdb.export_test_results(...)
         specifying additional parameters for exporting test results to BigQuery.
         Will always upload to the following tables in addition to any tables
         specified by the list's elements:
           chrome-luci-data.chromium.ci_test_results
           chrome-luci-data.chromium.gpu_ci_test_results
-      experiments - a dict of experiment name to the percentage chance (0-100)
+      experiments: a dict of experiment name to the percentage chance (0-100)
         that it will apply to builds generated from this builder.
+      **kwargs: Additional keyword arguments that will be forwarded on to
+        `builders.builder`.
     """
     if not branches.matches(branch_selector):
         return
@@ -85,11 +98,12 @@
     if try_only_kwargs:
         fail("CI builders cannot specify the following try-only arguments: {}".format(try_only_kwargs))
 
-    # Branch builders should never close the tree, only builders from the main
-    # "ci" bucket.
-    bucket = defaults.get_value_from_kwargs("bucket", kwargs)
-    if tree_closing and bucket == "ci":
-        notifies = (notifies or []) + ["chromium-tree-closer", "chromium-tree-closer-email"]
+    tree_closing = defaults.get_value("tree_closing", tree_closing)
+    if tree_closing:
+        tree_closing_notifiers = defaults.get_value("tree_closing_notifiers", tree_closing_notifiers, merge = args.MERGE_LIST)
+        tree_closing_notifiers = args.listify("chromium-tree-closer", "chromium-tree-closer-email", tree_closing_notifiers)
+
+        notifies = args.listify(notifies, tree_closing_notifiers)
 
     merged_resultdb_bigquery_exports = [
         resultdb.export_test_results(
@@ -117,7 +131,7 @@
     ]
     merged_resultdb_bigquery_exports.extend(resultdb_bigquery_exports or [])
 
-    sheriff_rotations = listify(
+    sheriff_rotations = args.listify(
         sheriff_rotations,
         # All CI builders on standard branches should be part of the
         # chrome_browser_release sheriff rotation
@@ -160,12 +174,16 @@
             if console_view == None:
                 console_view = defaults.get_value_from_kwargs("builder_group", kwargs)
 
+            bucket = defaults.get_value_from_kwargs("bucket", kwargs)
             builder = "{}/{}".format(bucket, name)
 
             overview_console_category = console_view
             if entry.category:
                 overview_console_category = "|".join([console_view, entry.category])
-            main_console_view = defaults.get_value("main_console_view", main_console_view)
+            if not settings.is_main:
+                main_console_view = "main"
+            else:
+                main_console_view = defaults.get_value("main_console_view", main_console_view)
             if main_console_view:
                 luci.console_view_entry(
                     builder = builder,
@@ -186,702 +204,71 @@
                     short_name = entry.short_name,
                 )
 
-def android_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        # TODO(tandrii): migrate to this gradually (current value of
-        # goma.jobs.MANY_JOBS_FOR_CI is 500).
-        # goma_jobs=goma.jobs.MANY_JOBS_FOR_CI
-        goma_jobs = builders.goma.jobs.J150,
-        **kwargs):
-    kwargs.setdefault("os", os.LINUX_BIONIC_REMOVE)
-    return ci_builder(
-        name = name,
-        builder_group = "chromium.android",
-        goma_backend = goma_backend,
-        goma_jobs = goma_jobs,
-        sheriff_rotations = builders.sheriff_rotations.ANDROID,
-        **kwargs
-    )
+def _gpu_linux_builder(*, name, **kwargs):
+    """Defines a GPU-related linux builder.
 
-def android_fyi_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    kwargs.setdefault("os", os.LINUX_BIONIC_REMOVE)
-    return ci_builder(
-        name = name,
-        builder_group = "chromium.android.fyi",
-        goma_backend = goma_backend,
-        **kwargs
-    )
+    This sets linux-specific defaults that are common to GPU-related builder
+    groups.
+    """
+    kwargs.setdefault("cores", 8)
+    kwargs.setdefault("os", os.LINUX_BIONIC_SWITCH_TO_DEFAULT)
+    return ci.builder(name = name, **kwargs)
 
-def angle_builder(*, name, **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.angle",
-        executable = "recipe:angle_chromium",
-        service_account =
-            "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        properties = {
-            "perf_dashboard_machine_group": "ChromiumANGLE",
-        },
-        **kwargs
-    )
+def _gpu_mac_builder(*, name, **kwargs):
+    """Defines a GPU-related mac builder.
 
-def angle_linux_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    return angle_builder(
-        name = name,
-        goma_backend = goma_backend,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
+    This sets mac-specific defaults that are common to GPU-related builder
+    groups.
+    """
+    kwargs.setdefault("os", os.MAC_ANY)
+    kwargs.setdefault("pool", ci.DEFAULT_POOL)
+    return ci.builder(name = name, **kwargs)
 
-def angle_mac_builder(*, name, **kwargs):
-    return angle_builder(
-        name = name,
-        builderless = False,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
+def _gpu_windows_builder(*, name, **kwargs):
+    """Defines a GPU-related windows builder.
 
-# ANGLE testers are thin testers, they use linux VMs regardless of the
-# actual OS that the tests are built for
-def angle_thin_tester(
-        *,
-        name,
-        **kwargs):
-    return angle_linux_builder(
-        name = name,
-        cores = 2,
-        # Setting goma_backend for testers is a no-op, but better to be explicit
-        # here and also leave the generated configs unchanged for these testers.
-        goma_backend = None,
-        **kwargs
-    )
-
-def angle_windows_builder(*, name, **kwargs):
-    return angle_builder(
-        name = name,
-        builderless = True,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_ANY,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def cipd_builder(*, name, **kwargs):
-    return ci_builder(
-        name = name,
-        builder_group = "chromium.packager",
-        service_account = "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def cipd_3pp_builder(*, name, os, properties, **kwargs):
-    return cipd_builder(
-        name = name,
-        executable = "recipe:chromium_3pp",
-        os = os,
-        properties = properties,
-        **kwargs
-    )
-
-def chromium_builder(*, name, tree_closing = True, **kwargs):
-    return ci_builder(
-        name = name,
-        builder_group = "chromium",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM,
-        tree_closing = tree_closing,
-        **kwargs
-    )
-
-def chromiumos_builder(
-        *,
-        name,
-        tree_closing = True,
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM,
-        **kwargs):
-    kwargs.setdefault("os", os.LINUX_BIONIC_REMOVE)
-    return ci_builder(
-        name = name,
-        builder_group = "chromium.chromiumos",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        sheriff_rotations = sheriff_rotations,
-        tree_closing = tree_closing,
-        **kwargs
-    )
-
-def clang_builder(*, name, builderless = True, cores = 32, properties = None, **kwargs):
-    properties = properties or {}
-    properties.update({
-        "perf_dashboard_machine_group": "ChromiumClang",
-    })
-    return ci_builder(
-        name = name,
-        builder_group = "chromium.clang",
-        builderless = builderless,
-        cores = cores,
-        # Because these run ToT Clang, goma is not used.
-        # Naturally the runtime will be ~4-8h on average, depending on config.
-        # CFI builds will take even longer - around 11h.
-        execution_timeout = 14 * time.hour,
-        properties = properties,
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM_CLANG,
-        **kwargs
-    )
-
-def clang_mac_builder(*, name, cores = 24, **kwargs):
-    return clang_builder(
-        name = name,
-        cores = cores,
-        os = builders.os.MAC_DEFAULT,
-        ssd = True,
-        properties = {
-            # The Chromium build doesn't need system Xcode, but the ToT clang
-            # bots also build clang and llvm and that build does need system
-            # Xcode.
-            "xcode_build_version": "12d4e",
-        },
-        **kwargs
-    )
-
-def dawn_builder(*, name, **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.dawn",
-        service_account =
-            "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def dawn_linux_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    return dawn_builder(
-        name = name,
-        builderless = True,
-        goma_backend = goma_backend,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def dawn_mac_builder(*, name, **kwargs):
-    return dawn_builder(
-        name = name,
-        builderless = False,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-# Many of the GPU testers are thin testers, they use linux VMS regardless of the
-# actual OS that the tests are built for
-def dawn_thin_tester(
-        *,
-        name,
-        **kwargs):
-    return dawn_linux_builder(
-        name = name,
-        cores = 2,
-        # Setting goma_backend for testers is a no-op, but better to be explicit
-        goma_backend = None,
-        **kwargs
-    )
-
-def dawn_windows_builder(*, name, **kwargs):
-    return dawn_builder(
-        name = name,
-        builderless = True,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_ANY,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def fuzz_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.fuzz",
-        goma_backend = goma_backend,
-        notifies = ["chromesec-lkgr-failures"],
-        **kwargs
-    )
-
-def fuzz_libfuzzer_builder(*, name, **kwargs):
-    return fuzz_builder(
-        name = name,
-        executable = "recipe:chromium_libfuzzer",
-        **kwargs
-    )
-
-def fyi_builder(
-        *,
-        name,
-        execution_timeout = 10 * time.hour,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    kwargs.setdefault("os", os.LINUX_BIONIC_REMOVE)
-
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.fyi",
-        execution_timeout = execution_timeout,
-        goma_backend = goma_backend,
-        **kwargs
-    )
-
-def fyi_celab_builder(*, name, **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.fyi",
-        os = builders.os.WINDOWS_ANY,
-        executable = "recipe:celab",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        properties = {
-            "exclude": "chrome_only",
-            "pool_name": "celab-chromium-ci",
-            "pool_size": 20,
-            "tests": "*",
-        },
-        **kwargs
-    )
-
-def fyi_coverage_builder(
-        *,
-        name,
-        cores = 32,
-        ssd = True,
-        execution_timeout = 20 * time.hour,
-        **kwargs):
-    return fyi_builder(
-        name = name,
-        cores = cores,
-        ssd = ssd,
-        execution_timeout = execution_timeout,
-        **kwargs
-    )
-
-def fyi_ios_builder(
-        *,
-        name,
-        executable = "recipe:chromium",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_11,
-        xcode = builders.xcode.x13main,
-        **kwargs):
-    return fyi_builder(
-        name = name,
-        cores = None,
-        executable = executable,
-        os = os,
-        xcode = xcode,
-        **kwargs
-    )
-
-def fyi_mac_builder(
-        *,
-        name,
-        cores = 4,
-        os = builders.os.MAC_DEFAULT,
-        **kwargs):
-    if not "goma_backend" in kwargs:
-        kwargs["goma_backend"] = builders.goma.backend.RBE_PROD
-    return fyi_builder(
-        name = name,
-        cores = cores,
-        os = os,
-        **kwargs
-    )
-
-def fyi_windows_builder(
-        *,
-        name,
-        os = builders.os.WINDOWS_DEFAULT,
-        **kwargs):
-    return fyi_builder(
-        name = name,
-        os = os,
-        **kwargs
-    )
-
-def gpu_fyi_builder(*, name, **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.gpu.fyi",
-        service_account =
-            "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM_GPU,
-        properties = {
-            "perf_dashboard_machine_group": "ChromiumGPUFYI",
-        },
-        **kwargs
-    )
-
-def gpu_fyi_linux_builder(
-        *,
-        name,
-        execution_timeout = 6 * time.hour,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    return gpu_fyi_builder(
-        name = name,
-        execution_timeout = execution_timeout,
-        goma_backend = goma_backend,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def gpu_fyi_mac_builder(*, name, **kwargs):
-    return gpu_fyi_builder(
-        name = name,
-        builderless = False,
-        cores = None,
-        execution_timeout = 6 * time.hour,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-# Many of the GPU testers are thin testers, they use linux VMS regardless of the
-# actual OS that the tests are built for
-def gpu_fyi_thin_tester(
-        *,
-        name,
-        execution_timeout = 6 * time.hour,
-        **kwargs):
-    return gpu_fyi_linux_builder(
-        name = name,
-        cores = 2,
-        execution_timeout = execution_timeout,
-        # Setting goma_backend for testers is a no-op, but better to be explicit
-        # here and also leave the generated configs unchanged for these testers.
-        goma_backend = None,
-        **kwargs
-    )
-
-def gpu_fyi_windows_builder(*, name, **kwargs):
-    return gpu_fyi_builder(
-        name = name,
-        builderless = True,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_ANY,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def gpu_builder(*, name, tree_closing = True, notifies = None, **kwargs):
-    if tree_closing:
-        notifies = (notifies or []) + ["gpu-tree-closer-email"]
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.gpu",
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM_GPU,
-        tree_closing = tree_closing,
-        notifies = notifies,
-        **kwargs
-    )
-
-def gpu_linux_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    return gpu_builder(
-        name = name,
-        builderless = True,
-        goma_backend = goma_backend,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def gpu_mac_builder(*, name, **kwargs):
-    return gpu_builder(
-        name = name,
-        builderless = False,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-# Many of the GPU testers are thin testers, they use linux VMS regardless of the
-# actual OS that the tests are built for
-def gpu_thin_tester(*, name, tree_closing = True, **kwargs):
-    return gpu_linux_builder(
-        name = name,
-        cores = 2,
-        tree_closing = tree_closing,
-        # Setting goma_backend for testers is a no-op, but better to be explicit
-        goma_backend = None,
-        **kwargs
-    )
-
-def gpu_windows_builder(*, name, **kwargs):
-    return gpu_builder(
-        name = name,
-        builderless = True,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_ANY,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def infra_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_BIONIC_REMOVE,
-        **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "infra",
-        goma_backend = goma_backend,
-        os = os,
-        **kwargs
-    )
-
-def linux_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        goma_jobs = builders.goma.jobs.MANY_JOBS_FOR_CI,
-        tree_closing = True,
-        notifies = ("chromium.linux",),
-        extra_notifies = None,
-        **kwargs):
-    kwargs.setdefault("os", builders.os.LINUX_BIONIC_REMOVE)
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.linux",
-        goma_backend = goma_backend,
-        goma_jobs = goma_jobs,
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM,
-        tree_closing = tree_closing,
-        notifies = list(notifies) + (extra_notifies or []),
-        **kwargs
-    )
-
-def mac_builder(
-        *,
-        name,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_DEFAULT,
-        sheriff_rotations = None,
-        tree_closing = True,
-        **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.mac",
-        cores = cores,
-        goma_backend = goma_backend,
-        os = os,
-        sheriff_rotations = listify(builders.sheriff_rotations.CHROMIUM, sheriff_rotations),
-        tree_closing = tree_closing,
-        **kwargs
-    )
-
-def mac_ios_builder(
-        *,
-        name,
-        executable = "recipe:chromium",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_11,
-        xcode = builders.xcode.x13main,
-        **kwargs):
-    return mac_builder(
-        name = name,
-        goma_backend = goma_backend,
-        executable = executable,
-        os = os,
-        sheriff_rotations = builders.sheriff_rotations.IOS,
-        xcode = xcode,
-        **kwargs
-    )
-
-def mac_thin_tester(
-        *,
-        name,
-        triggered_by,
-        **kwargs):
-    return thin_tester(
-        name = name,
-        builder_group = "chromium.mac",
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM,
-        triggered_by = triggered_by,
-        **kwargs
-    )
-
-def memory_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        goma_jobs = builders.goma.jobs.MANY_JOBS_FOR_CI,
-        notifies = None,
-        sheriff_rotations = None,
-        tree_closing = True,
-        **kwargs):
-    if name.startswith("Linux"):
-        kwargs.setdefault("os", builders.os.LINUX_BIONIC_REMOVE)
-        notifies = (notifies or []) + ["linux-memory"]
-
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.memory",
-        goma_backend = goma_backend,
-        goma_jobs = goma_jobs,
-        notifies = notifies,
-        sheriff_rotations = listify(builders.sheriff_rotations.CHROMIUM, sheriff_rotations),
-        tree_closing = tree_closing,
-        **kwargs
-    )
-
-def mojo_builder(
-        *,
-        name,
-        execution_timeout = 10 * time.hour,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.mojo",
-        execution_timeout = execution_timeout,
-        goma_backend = goma_backend,
-        **kwargs
-    )
-
-def rust_builder(
-        *,
-        name,
-        **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.rust",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs
-    )
-
-def swangle_builder(*, name, builderless = True, pinned = True, **kwargs):
-    builder_args = dict(kwargs)
-    builder_args.update(
-        name = name,
-        builder_group = "chromium.swangle",
-        builderless = builderless,
-        service_account =
-            "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM_GPU,
-    )
-    if pinned:
-        builder_args.update(executable = "recipe:angle_chromium")
-    return ci.builder(**builder_args)
-
-def swangle_linux_builder(
-        *,
-        name,
-        **kwargs):
-    return swangle_builder(
-        name = name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
-
-def swangle_mac_builder(
-        *,
-        name,
-        **kwargs):
-    return swangle_builder(
-        name = name,
-        builderless = False,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-def swangle_windows_builder(*, name, **kwargs):
-    return swangle_builder(
-        name = name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_ANY,
-        pool = "luci.chromium.gpu.ci",
-        **kwargs
-    )
+    This sets windows-specific defaults that are common to GPU-related builder
+    groups.
+    """
+    kwargs.setdefault("builderless", True)
+    kwargs.setdefault("cores", 8)
+    kwargs.setdefault("os", os.WINDOWS_ANY)
+    return ci.builder(name = name, **kwargs)
 
 def thin_tester(
         *,
         name,
         triggered_by,
-        builder_group,
-        os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-        tree_closing = True,
+        cores = args.DEFAULT,
         **kwargs):
+    """Define a thin tester.
+
+    A thin tester is a builder that is triggered by another builder to
+    trigger and wait for tests that were built by the triggering builder.
+    Because these builders do not perform compilation and are performing
+    platform-agnostic operations, they can generally run on small linux
+    machines.
+
+    Args:
+      name: The name of the builder.
+      triggered_by: The triggering builder. See
+        https://chromium.googlesource.com/infra/luci/luci-go/+/refs/heads/main/lucicfg/doc/README.md#luci.builder
+        for more information.
+      cores: See `builders.builder` for more information. The `thin_tester_core`
+        module-level default in `ci.defaults` will be used as the default if it
+        is set.
+      **kwargs: Additional keyword arguments to forward on to `ci.builder`.
+
+    Returns:
+      The `luci.builder` keyset.
+    """
+    cores = defaults.get_value("thin_tester_cores", cores)
+    kwargs.setdefault("goma_backend", None)
+    kwargs.setdefault("os", builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT)
     return ci.builder(
         name = name,
-        builder_group = builder_group,
         triggered_by = triggered_by,
-        goma_backend = None,
-        os = os,
-        tree_closing = tree_closing,
-        **kwargs
-    )
-
-def updater_builder(
-        *,
-        name,
-        **kwargs):
-    kwargs.setdefault("os", os.LINUX_BIONIC_REMOVE)
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.updater",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs
-    )
-
-def win_builder(
-        *,
-        name,
-        os = builders.os.WINDOWS_DEFAULT,
-        tree_closing = True,
-        **kwargs):
-    return ci.builder(
-        name = name,
-        builder_group = "chromium.win",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = os,
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM,
-        tree_closing = tree_closing,
-        **kwargs
-    )
-
-def win_thin_tester(*, name, triggered_by, **kwargs):
-    return thin_tester(
-        name = name,
-        builder_group = "chromium.win",
-        sheriff_rotations = builders.sheriff_rotations.CHROMIUM,
-        triggered_by = triggered_by,
+        cores = cores,
         **kwargs
     )
 
@@ -891,55 +278,23 @@
 
     # Functions for declaring CI builders
     builder = ci_builder,
-
-    # More specific builder wrapper functions
-    android_builder = android_builder,
-    android_fyi_builder = android_fyi_builder,
-    angle_linux_builder = angle_linux_builder,
-    angle_mac_builder = angle_mac_builder,
-    angle_thin_tester = angle_thin_tester,
-    angle_windows_builder = angle_windows_builder,
-    chromium_builder = chromium_builder,
-    chromiumos_builder = chromiumos_builder,
-    cipd_3pp_builder = cipd_3pp_builder,
-    cipd_builder = cipd_builder,
-    clang_builder = clang_builder,
-    clang_mac_builder = clang_mac_builder,
-    dawn_linux_builder = dawn_linux_builder,
-    dawn_mac_builder = dawn_mac_builder,
-    dawn_thin_tester = dawn_thin_tester,
-    dawn_windows_builder = dawn_windows_builder,
-    fuzz_builder = fuzz_builder,
-    fuzz_libfuzzer_builder = fuzz_libfuzzer_builder,
-    fyi_builder = fyi_builder,
-    fyi_celab_builder = fyi_celab_builder,
-    fyi_coverage_builder = fyi_coverage_builder,
-    fyi_ios_builder = fyi_ios_builder,
-    fyi_mac_builder = fyi_mac_builder,
-    fyi_windows_builder = fyi_windows_builder,
-    gpu_fyi_linux_builder = gpu_fyi_linux_builder,
-    gpu_fyi_mac_builder = gpu_fyi_mac_builder,
-    gpu_fyi_thin_tester = gpu_fyi_thin_tester,
-    gpu_fyi_windows_builder = gpu_fyi_windows_builder,
-    gpu_linux_builder = gpu_linux_builder,
-    gpu_mac_builder = gpu_mac_builder,
-    gpu_thin_tester = gpu_thin_tester,
-    gpu_windows_builder = gpu_windows_builder,
-    infra_builder = infra_builder,
-    linux_builder = linux_builder,
-    mac_builder = mac_builder,
-    mac_ios_builder = mac_ios_builder,
-    mac_thin_tester = mac_thin_tester,
-    memory_builder = memory_builder,
-    mojo_builder = mojo_builder,
-    rust_builder = rust_builder,
-    swangle_linux_builder = swangle_linux_builder,
-    swangle_mac_builder = swangle_mac_builder,
-    swangle_windows_builder = swangle_windows_builder,
     thin_tester = thin_tester,
-    updater_builder = updater_builder,
-    win_builder = win_builder,
-    win_thin_tester = win_thin_tester,
+
+    # CONSTANTS
+    DEFAULT_EXECUTABLE = "recipe:chromium",
+    DEFAULT_EXECUTION_TIMEOUT = 3 * time.hour,
+    DEFAULT_POOL = "luci.chromium.ci",
+    DEFAULT_SERVICE_ACCOUNT = "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com",
+
+    # Functions and constants for the GPU-related builder groups
+    gpu = struct(
+        linux_builder = _gpu_linux_builder,
+        mac_builder = _gpu_mac_builder,
+        windows_builder = _gpu_windows_builder,
+        POOL = "luci.chromium.gpu.ci",
+        SERVICE_ACCOUNT = "chromium-ci-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
+        TREE_CLOSING_NOTIFIERS = ["gpu-tree-closer-email"],
+    ),
 )
 
 rbe_instance = struct(
diff --git a/infra/config/lib/listify.star b/infra/config/lib/listify.star
deleted file mode 100644
index 6afcd92..0000000
--- a/infra/config/lib/listify.star
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright 2021 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-def listify(*args):
-    """Create a single list from multiple arguments.
-
-    Each argument can be either a single element or a list of elements. A single
-    element will appear as an element in the resulting list iff it is non-None.
-    A list of elements will have all non-None elements appear in the resulting
-    list.
-    """
-    l = []
-    for a in args:
-        if type(a) != type([]):
-            a = [a]
-        for e in a:
-            if e != None:
-                l.append(e)
-    return l
diff --git a/infra/config/lib/try.star b/infra/config/lib/try.star
index f83a765..7f39875 100644
--- a/infra/config/lib/try.star
+++ b/infra/config/lib/try.star
@@ -222,358 +222,32 @@
             includable_only = True,
         )
 
-def blink_builder(*, name, goma_backend = None, **kwargs):
-    kwargs.setdefault("os", builders.os.LINUX_BIONIC_REMOVE)
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.blink",
-        goma_backend = goma_backend,
-        **kwargs
-    )
+def _gpu_optional_tests_builder(*, name, **kwargs):
+    kwargs.setdefault("builderless", False)
+    kwargs.setdefault("execution_timeout", 6 * time.hour)
+    kwargs.setdefault("service_account", try_.gpu.SERVICE_ACCOUNT)
+    return try_.builder(name = name, **kwargs)
 
-def blink_mac_builder(
-        *,
-        name,
-        os = builders.os.MAC_ANY,
-        builderless = True,
-        **kwargs):
-    return blink_builder(
-        name = name,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = os,
-        builderless = builderless,
-        ssd = True,
-        **kwargs
-    )
-
-def chromium_builder(*, name, **kwargs):
-    kwargs.setdefault("os", builders.os.LINUX_BIONIC_REMOVE)
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium",
-        builderless = True,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs
-    )
-
-def chromium_android_builder(*, name, **kwargs):
-    kwargs.setdefault("os", os.LINUX_BIONIC_REMOVE)
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.android",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs
-    )
-
-def chromium_angle_builder(*, name, **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.angle",
-        builderless = False,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        goma_jobs = builders.goma.jobs.J150,
-        service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def chromium_angle_pinned_builder(*, name, **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.angle",
-        builderless = True,
-        executable = "recipe:angle_chromium_trybot",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def chromium_angle_mac_builder(*, name, **kwargs):
-    return chromium_angle_pinned_builder(
-        name = name,
-        cores = None,
-        ssd = None,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-def chromium_angle_ios_builder(*, name, **kwargs):
-    return chromium_angle_mac_builder(
-        name = name,
-        xcode = builders.xcode.x12a7209,
-        **kwargs
-    )
-
-def chromium_chromiumos_builder(*, name, **kwargs):
-    kwargs.setdefault("os", builders.os.LINUX_BIONIC_REMOVE)
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.chromiumos",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs
-    )
-
-def chromium_dawn_builder(*, name, **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.dawn",
-        builderless = False,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def chromium_dawn_builderless_builder(*, name, **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.dawn",
-        builderless = True,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def chromium_linux_builder(*, name, goma_backend = builders.goma.backend.RBE_PROD, **kwargs):
-    kwargs.setdefault("os", builders.os.LINUX_BIONIC_REMOVE)
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.linux",
-        goma_backend = goma_backend,
-        **kwargs
-    )
-
-def chromium_mac_builder(
-        *,
-        name,
-        builderless = True,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        ssd = True,
-        **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.mac",
-        cores = cores,
-        goma_backend = goma_backend,
-        os = os,
-        builderless = builderless,
-        ssd = ssd,
-        **kwargs
-    )
-
-def chromium_mac_ios_builder(
-        *,
-        name,
-        executable = "recipe:chromium_trybot",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_11,
-        xcode = builders.xcode.x13main,
-        **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.mac",
-        cores = None,
-        executable = executable,
-        goma_backend = goma_backend,
-        os = os,
-        xcode = xcode,
-        **kwargs
-    )
-
-def chromium_rust_builder(
-        *,
-        name,
-        **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.rust",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        **kwargs
-    )
-
-def chromium_swangle_builder(*, name, pinned = True, **kwargs):
-    builder_args = dict(kwargs)
-    builder_args.update(
-        name = name,
-        builder_group = "tryserver.chromium.swangle",
-        builderless = True,
-        service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-    )
-    if pinned:
-        builder_args.update(executable = "recipe:angle_chromium_trybot")
-    return try_builder(**builder_args)
-
-def chromium_swangle_linux_builder(*, name, **kwargs):
-    return chromium_swangle_builder(
-        name = name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_BIONIC_REMOVE,
-        **kwargs
-    )
-
-def chromium_swangle_mac_builder(*, name, **kwargs):
-    return chromium_swangle_builder(
-        name = name,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-def chromium_swangle_windows_builder(*, name, **kwargs):
-    return chromium_swangle_builder(
-        name = name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_DEFAULT,
-        **kwargs
-    )
-
-def chromium_updater_builder(
-        *,
-        name,
-        executable = "recipe:chromium_trybot",
-        goma_backend,
-        os,
-        **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.updater",
-        builderless = True,
-        executable = executable,
-        goma_backend = goma_backend,
-        os = os,
-        **kwargs
-    )
-
-def chromium_updater_mac_builder(*, name, **kwargs):
-    return chromium_updater_builder(
-        name = name,
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-def chromium_updater_win_builder(*, name, **kwargs):
-    return chromium_updater_builder(
-        name = name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_DEFAULT,
-        **kwargs
-    )
-
-def chromium_win_builder(
-        *,
-        name,
-        builderless = True,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.WINDOWS_DEFAULT,
-        **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.win",
-        builderless = builderless,
-        goma_backend = goma_backend,
-        os = os,
-        **kwargs
-    )
-
-def cipd_builder(*, name, **kwargs):
-    return try_builder(
-        name = name,
-        service_account = "chromium-cipd-try-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def cipd_3pp_builder(*, name, os, properties, **kwargs):
-    return cipd_builder(
-        name = name,
-        builder_group = "tryserver.chromium.packager",
-        executable = "recipe:chromium_3pp",
-        os = os,
-        properties = properties,
-        **kwargs
-    )
-
-def gpu_try_builder(*, name, builderless = False, execution_timeout = 6 * time.hour, **kwargs):
-    return try_builder(
-        name = name,
-        builderless = builderless,
-        execution_timeout = execution_timeout,
-        service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
-        **kwargs
-    )
-
-def gpu_chromium_android_builder(*, name, **kwargs):
-    return gpu_try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.android",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_BIONIC_REMOVE,
-        **kwargs
-    )
-
-def gpu_chromium_linux_builder(*, name, **kwargs):
-    return gpu_try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.linux",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_BIONIC_REMOVE,
-        **kwargs
-    )
-
-def gpu_chromium_mac_builder(*, name, **kwargs):
-    return gpu_try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.mac",
-        cores = None,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.MAC_ANY,
-        **kwargs
-    )
-
-def gpu_chromium_win_builder(*, name, os = builders.os.WINDOWS_ANY, **kwargs):
-    return gpu_try_builder(
-        name = name,
-        builder_group = "tryserver.chromium.win",
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = os,
-        **kwargs
-    )
-
-def infra_builder(
-        *,
-        name,
-        goma_backend = builders.goma.backend.RBE_PROD,
-        os = builders.os.LINUX_BIONIC_REMOVE,
-        **kwargs):
-    return try_builder(
-        name = name,
-        builder_group = "tryserver.infra",
-        goma_backend = goma_backend,
-        os = os,
-        **kwargs
-    )
-
+# TODO(gbeaty) Replace with separate functions for orchestrator and compilator
+# so that the arguments interact with the module-level defaults in a more
+# standard manner
 def orchestrator_pair_builders(
         *,
         name,
-        builder_group_func,
-        orchestrator_builder_group,
-        orchestrator_cores,
-        orchestrator_tryjob,
-        compilator_cores,
-        compilator_name,
-        compilator_os,
+        orchestrator_cores = None,
+        orchestrator_tryjob = None,
+        compilator_cores = None,
+        compilator_name = None,
         compilator_goma_jobs = None,
-        compilator_grace_period = defaults.get_value("grace_period", None),
+        compilator_grace_period = None,
         compilator_builderless = not settings.is_main,
         orchestrator_builderless = not settings.is_main,
-        **common_kwargs):
-    common_description = common_kwargs.pop("description_html", "")
+        **kwargs):
+    builder_group = defaults.get_value_from_kwargs("builder_group", kwargs)
+    if not builder_group:
+        fail("builder_group must be specified")
+
+    common_description = kwargs.pop("description_html", "")
     if common_description:
         common_description += "<br>"
     orchestrator_url = builder_url("try", name)
@@ -593,116 +267,53 @@
         )
     )
 
-    orchestrator_builder = builder_group_func(
-        name = name,
+    orchestrator_kwargs = dict(kwargs)
+    orchestrator_kwargs.update(dict(
+        description_html = orchestrator_description,
         executable = "recipe:chromium/orchestrator",
-        cores = orchestrator_cores,
-        builderless = orchestrator_builderless,
+        # TODO(gbeaty) After prod freeze, remove goma details, the
+        # orchestrator doesn't compile
+        # goma_backend = None,
+        os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
         properties = {
             "$build/chromium_orchestrator": {
                 "compilator": compilator_name,
                 "compilator_watcher_git_revision": compilator_watcher_git_revision,
             },
         },
-        tryjob = orchestrator_tryjob,
         service_account = "chromium-orchestrator@chops-service-accounts.iam.gserviceaccount.com",
-        os = os.LINUX_BIONIC,
-        description_html = orchestrator_description,
         ssd = None,
-        **common_kwargs
+    ))
+    orchestrator_builder = try_.builder(
+        name = name,
+        cores = orchestrator_cores,
+        builderless = orchestrator_builderless,
+        tryjob = orchestrator_tryjob,
+        **orchestrator_kwargs
     )
-    compilator_builder = builder_group_func(
-        name = compilator_name,
+
+    compilator_kwargs = dict(kwargs)
+    compilator_kwargs.update(dict(
         executable = "recipe:chromium/compilator",
-        cores = compilator_cores,
-        builderless = compilator_builderless,
-        goma_jobs = compilator_goma_jobs,
-        ssd = True,
         properties = {
             "orchestrator": {
                 "builder_name": name,
-                "builder_group": orchestrator_builder_group,
+                "builder_group": builder_group,
             },
         },
-        os = compilator_os,
+        ssd = True,
+    ))
+    compilator_builder = try_.builder(
+        name = compilator_name,
+        cores = compilator_cores,
+        builderless = compilator_builderless,
+        goma_jobs = compilator_goma_jobs,
         description_html = compilator_description,
         grace_period = compilator_grace_period,
-        **common_kwargs
+        **compilator_kwargs
     )
     return orchestrator_builder, compilator_builder
 
-def chromium_chromiumos_orchestrator_pair(
-        **kwargs):
-    return orchestrator_pair_builders(
-        builder_group_func = chromium_chromiumos_builder,
-        orchestrator_builder_group = "tryserver.chromium.chromiumos",
-        compilator_os = os.LINUX_BIONIC,
-        **kwargs
-    )
-
-def chromium_linux_orchestrator_pair(
-        **kwargs):
-    return orchestrator_pair_builders(
-        builder_group_func = chromium_linux_builder,
-        orchestrator_builder_group = "tryserver.chromium.linux",
-        compilator_os = os.LINUX_BIONIC,
-        **kwargs
-    )
-
-def chromium_win_orchestrator_pair(
-        **kwargs):
-    return orchestrator_pair_builders(
-        builder_group_func = chromium_win_builder,
-        orchestrator_builder_group = "tryserver.chromium.win",
-        compilator_os = os.WINDOWS_10,
-        **kwargs
-    )
-
-def chromium_android_orchestrator_pair(
-        **kwargs):
-    return orchestrator_pair_builders(
-        builder_group_func = chromium_android_builder,
-        orchestrator_builder_group = "tryserver.chromium.android",
-        compilator_os = os.LINUX_BIONIC,
-        **kwargs
-    )
-
-def chromium_mac_orchestrator_pair(
-        **kwargs):
-    return orchestrator_pair_builders(
-        builder_group_func = chromium_mac_builder,
-        orchestrator_builder_group = "tryserver.chromium.mac",
-        compilator_cores = None,
-        **kwargs
-    )
-
-def presubmit_builder(*, name, tryjob, os = builders.os.LINUX_BIONIC_SWITCH_TO_DEFAULT, **kwargs):
-    """Define a presubmit builder.
-
-    Presubmit builders are builders that run fast checks that don't require
-    building. Their results aren't re-used because they tend to provide guards
-    against generated files being out of date, so they MUST run quickly so that
-    the submit after a CQ dry run doesn't take long.
-    """
-    tryjob_args = {a: getattr(tryjob, a) for a in dir(tryjob)}
-    tryjob_args["disable_reuse"] = True
-    tryjob_args["add_default_excludes"] = False
-    tryjob = try_.job(**tryjob_args)
-
-    return try_builder(
-        name = name,
-        list_view = "presubmit",
-        main_list_view = "try",
-        os = os,
-        # Default priority for buildbucket is 30, see
-        # https://chromium.googlesource.com/infra/infra/+/bb68e62b4380ede486f65cd32d9ff3f1bbe288e4/appengine/cr-buildbucket/creation.py#42
-        # This will improve our turnaround time for landing infra/config changes
-        # when addressing outages
-        priority = 25,
-        tryjob = tryjob,
-        **kwargs
-    )
-
 try_ = struct(
     # Module-level defaults for try functions
     defaults = defaults,
@@ -710,40 +321,15 @@
     # Functions for declaring try builders
     builder = try_builder,
     job = tryjob,
+    orchestrator_pair_builders = orchestrator_pair_builders,
 
-    # More specific builder wrapper functions
-    blink_builder = blink_builder,
-    blink_mac_builder = blink_mac_builder,
-    chromium_builder = chromium_builder,
-    chromium_android_builder = chromium_android_builder,
-    chromium_android_orchestrator_pair = chromium_android_orchestrator_pair,
-    chromium_angle_builder = chromium_angle_builder,
-    chromium_angle_ios_builder = chromium_angle_ios_builder,
-    chromium_angle_mac_builder = chromium_angle_mac_builder,
-    chromium_angle_pinned_builder = chromium_angle_pinned_builder,
-    chromium_chromiumos_builder = chromium_chromiumos_builder,
-    chromium_chromiumos_orchestrator_pair = chromium_chromiumos_orchestrator_pair,
-    chromium_dawn_builder = chromium_dawn_builder,
-    chromium_dawn_builderless_builder = chromium_dawn_builderless_builder,
-    chromium_linux_builder = chromium_linux_builder,
-    chromium_linux_orchestrator_pair = chromium_linux_orchestrator_pair,
-    chromium_mac_builder = chromium_mac_builder,
-    chromium_mac_ios_builder = chromium_mac_ios_builder,
-    chromium_mac_orchestrator_pair = chromium_mac_orchestrator_pair,
-    chromium_rust_builder = chromium_rust_builder,
-    chromium_swangle_linux_builder = chromium_swangle_linux_builder,
-    chromium_swangle_mac_builder = chromium_swangle_mac_builder,
-    chromium_swangle_windows_builder = chromium_swangle_windows_builder,
-    chromium_updater_mac_builder = chromium_updater_mac_builder,
-    chromium_updater_win_builder = chromium_updater_win_builder,
-    chromium_win_builder = chromium_win_builder,
-    chromium_win_orchestrator_pair = chromium_win_orchestrator_pair,
-    cipd_3pp_builder = cipd_3pp_builder,
-    cipd_builder = cipd_builder,
-    gpu_chromium_android_builder = gpu_chromium_android_builder,
-    gpu_chromium_linux_builder = gpu_chromium_linux_builder,
-    gpu_chromium_mac_builder = gpu_chromium_mac_builder,
-    gpu_chromium_win_builder = gpu_chromium_win_builder,
-    infra_builder = infra_builder,
-    presubmit_builder = presubmit_builder,
+    # CONSTANTS
+    DEFAULT_EXECUTABLE = "recipe:chromium_trybot",
+    DEFAULT_EXECUTION_TIMEOUT = 4 * time.hour,
+    DEFAULT_POOL = "luci.chromium.try",
+    DEFAULT_SERVICE_ACCOUNT = "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com",
+    gpu = struct(
+        optional_tests_builder = _gpu_optional_tests_builder,
+        SERVICE_ACCOUNT = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
+    ),
 )
diff --git a/infra/config/subprojects/chromium/angle.try.star b/infra/config/subprojects/chromium/angle.try.star
index ca5b7e1c..057af24 100644
--- a/infra/config/subprojects/chromium/angle.try.star
+++ b/infra/config/subprojects/chromium/angle.try.star
@@ -2,12 +2,13 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-load("//lib/builders.star", "cpu", "os")
+load("//lib/builders.star", "cpu", "goma", "os", "xcode")
 load("//lib/try.star", "try_")
 
 try_.defaults.set(
     bucket = "try",
     build_numbers = True,
+    builder_group = "tryserver.chromium.angle",
     caches = [
         swarming.cache(
             name = "win_toolchain",
@@ -17,11 +18,12 @@
     cores = 8,
     cpu = cpu.X86_64,
     cq_group = "cq",
-    executable = "recipe:chromium_trybot",
+    executable = "recipe:angle_chromium_trybot",
     execution_timeout = 2 * time.hour,
     # Max. pending time for builds. CQ considers builds pending >2h as timed
     # out: http://shortn/_8PaHsdYmlq. Keep this in sync.
     expiration_timeout = 2 * time.hour,
+    goma_backend = goma.backend.RBE_PROD,
     os = os.LINUX_DEFAULT,
     pool = "luci.chromium.try",
     service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
@@ -29,7 +31,18 @@
     task_template_canary_percentage = 5,
 )
 
-try_.chromium_angle_ios_builder(
+def angle_mac_builder(*, name, **kwargs):
+    kwargs.setdefault("builderless", True)
+    kwargs.setdefault("cores", None)
+    kwargs.setdefault("os", os.MAC_ANY)
+    kwargs.setdefault("ssd", None)
+    return try_.builder(name = name, **kwargs)
+
+def angle_ios_builder(*, name, **kwargs):
+    kwargs.setdefault("xcode", xcode.x12a7209)
+    return angle_mac_builder(name = name, **kwargs)
+
+angle_ios_builder(
     name = "ios-angle-try-intel",
     pool = "luci.chromium.gpu.mac.mini.intel.uhd630.try",
 )
diff --git a/infra/config/subprojects/chromium/ci.star b/infra/config/subprojects/chromium/ci.star
index 5545453..2ef05571 100644
--- a/infra/config/subprojects/chromium/ci.star
+++ b/infra/config/subprojects/chromium/ci.star
@@ -3,35 +3,20 @@
 # found in the LICENSE file.
 
 load("//lib/branches.star", "branches")
-load("//lib/builders.star", "cpu", "goma", "os", "sheriff_rotations", "xcode")
-load("//lib/builder_config.star", "builder_config")
-load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/builders.star", "cpu")
+load("//lib/ci.star", "ci")
 load("//lib/consoles.star", "consoles")
-load("//console-header.star", "HEADER")
 load("//project.star", "settings")
 
-def main_console_if_on_branch():
-    return "main" if not settings.is_main else None
-
+# Bucket-wide defaults
 ci.defaults.set(
     bucket = "ci",
     build_numbers = True,
-    cores = 8,
     cpu = cpu.X86_64,
-    executable = "recipe:chromium",
-    execution_timeout = 3 * time.hour,
-    os = os.LINUX_DEFAULT,
-    pool = "luci.chromium.ci",
-    project_trigger_overrides = {"chromium": settings.project} if not settings.is_main else None,
-    service_account = "chromium-ci-builder@chops-service-accounts.iam.gserviceaccount.com",
+    project_trigger_overrides = branches.value({
+        branches.NOT_MAIN: {"chromium": settings.project},
+    }),
     triggered_by = ["chromium-gitiles-trigger"],
-    # TODO(crbug.com/1129723): set default goma_backend here.
-)
-
-consoles.defaults.set(
-    header = HEADER,
-    repo = "https://chromium.googlesource.com/chromium/src",
-    refs = [settings.ref],
 )
 
 luci.bucket(
@@ -68,8 +53,6 @@
     refs = [settings.ref],
 )
 
-# Automatically maintained consoles
-
 [consoles.overview_console_view(
     name = name,
     repo = "https://chromium.googlesource.com/chromium/src",
@@ -100,357 +83,6 @@
     ("mirrors", "{} CQ Mirrors Console".format(settings.project_title)),
 )]
 
-consoles.console_view(
-    name = "chromium",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    include_experimental_builds = True,
-    ordering = {
-        "*type*": consoles.ordering(short_names = ["dbg", "rel", "off"]),
-        "android": "*type*",
-        "fuchsia": "*type*",
-        "linux": "*type*",
-        "mac": "*type*",
-        "win": "*type*",
-    },
-)
-
-consoles.console_view(
-    name = "chromium.android",
-    branch_selector = branches.STANDARD_MILESTONE,
-    ordering = {
-        None: ["cronet", "builder", "tester"],
-        "*cpu*": ["arm", "arm64", "x86"],
-        "cronet": "*cpu*",
-        "builder": "*cpu*",
-        "builder|det": consoles.ordering(short_names = ["rel", "dbg"]),
-        "tester": ["phone", "tablet"],
-        "builder_tester|arm64": consoles.ordering(short_names = ["M proguard"]),
-    },
-)
-
-consoles.console_view(
-    name = "chromium.android.fyi",
-    ordering = {
-        None: ["android", "memory", "weblayer", "webview"],
-    },
-)
-
-consoles.console_view(
-    name = "chromium.angle",
-    ordering = {
-        None: ["Android", "Fuchsia", "Linux", "Mac", "iOS", "Windows", "Perf"],
-        "*builder*": ["Builder"],
-        "Android": "*builder*",
-        "Fuchsia": "*builder*",
-        "Linux": "*builder*",
-        "Mac": "*builder*",
-        "iOS": "*builder*",
-        "Windows": "*builder*",
-        "Perf": "*builder*",
-    },
-)
-
-consoles.console_view(
-    name = "chromium.chromiumos",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    ordering = {
-        None: ["default"],
-        "default": consoles.ordering(short_names = ["ful", "rel"]),
-        "simple": ["release", "debug"],
-    },
-)
-
-consoles.console_view(
-    name = "chromium.clang",
-    ordering = {
-        None: [
-            "ToT Linux",
-            "ToT Android",
-            "ToT Mac",
-            "ToT Windows",
-            "ToT Code Coverage",
-        ],
-        "ToT Linux": consoles.ordering(
-            short_names = ["rel", "ofi", "dbg", "asn", "fuz", "msn", "tsn"],
-        ),
-        "ToT Android": consoles.ordering(short_names = ["rel", "dbg", "x64"]),
-        "ToT Mac": consoles.ordering(short_names = ["rel", "ofi", "dbg"]),
-        "ToT Windows": consoles.ordering(
-            short_names = ["rel", "ofi"],
-            categories = ["x64"],
-        ),
-        "ToT Windows|x64": consoles.ordering(short_names = ["rel"]),
-        "CFI|Win": consoles.ordering(short_names = ["x86", "x64"]),
-        "iOS": ["public"],
-        "iOS|public": consoles.ordering(short_names = ["sim", "dev"]),
-    },
-)
-
-consoles.console_view(
-    name = "chromium.dawn",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    ordering = {
-        None: ["ToT"],
-        "*builder*": ["Builder"],
-        "*cpu*": consoles.ordering(short_names = ["x86"]),
-        "ToT|Mac": "*builder*",
-        "ToT|Windows|Builder": "*cpu*",
-        "ToT|Windows|Intel": "*cpu*",
-        "ToT|Windows|Nvidia": "*cpu*",
-        "DEPS|Mac": "*builder*",
-        "DEPS|Windows|Builder": "*cpu*",
-        "DEPS|Windows|Intel": "*cpu*",
-        "DEPS|Windows|Nvidia": "*cpu*",
-    },
-)
-
-consoles.console_view(
-    name = "chromium.fyi",
-    branch_selector = branches.STANDARD_MILESTONE,
-    ordering = {
-        None: [
-            "code_coverage",
-            "cronet",
-            "mac",
-            "deterministic",
-            "fuchsia",
-            "chromeos",
-            "iOS",
-            "infra",
-            "linux",
-            "recipe",
-            "site_isolation",
-            "network",
-            "viz",
-            "win10",
-            "win32",
-            "paeverywhere",
-            "backuprefptr",
-        ],
-        "code_coverage": consoles.ordering(
-            short_names = ["and", "ann", "lnx", "lcr", "jcr", "mac"],
-        ),
-        "mac": consoles.ordering(short_names = ["bld", "15", "herm"]),
-        "deterministic|mac": consoles.ordering(short_names = ["rel", "dbg"]),
-        "iOS|iOS13": consoles.ordering(short_names = ["dev", "sim"]),
-        "linux|blink": consoles.ordering(short_names = ["TD"]),
-    },
-)
-
-consoles.console_view(
-    name = "chromium.fuzz",
-    ordering = {
-        None: [
-            "afl",
-            "win asan",
-            "mac asan",
-            "cros asan",
-            "linux asan",
-            "libfuzz",
-            "linux msan",
-            "linux tsan",
-        ],
-        "*config*": consoles.ordering(short_names = ["dbg", "rel"]),
-        "win asan": "*config*",
-        "mac asan": "*config*",
-        "linux asan": "*config*",
-        "linux asan|x64 v8-ARM": "*config*",
-        "libfuzz": consoles.ordering(short_names = [
-            "chromeos-asan",
-            "linux32",
-            "linux32-dbg",
-            "linux",
-            "linux-dbg",
-            "linux-msan",
-            "linux-ubsan",
-            "mac-asan",
-            "win-asan",
-        ]),
-    },
-)
-
-consoles.console_view(
-    name = "chromium.gpu",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    ordering = {
-        None: ["Windows", "Mac", "Linux"],
-    },
-)
-
-consoles.console_view(
-    name = "chromium.gpu.fyi",
-    ordering = {
-        None: ["Windows", "Mac", "Linux"],
-        "*builder*": ["Builder"],
-        "*type*": consoles.ordering(short_names = ["rel", "dbg", "exp"]),
-        "*cpu*": consoles.ordering(short_names = ["x86"]),
-        "Windows": "*builder*",
-        "Windows|Builder": ["Release", "dx12vk", "Debug"],
-        "Windows|Builder|Release": "*cpu*",
-        "Windows|Builder|dx12vk": "*type*",
-        "Windows|Builder|Debug": "*cpu*",
-        "Windows|10|x64|Intel": "*type*",
-        "Windows|10|x64|Nvidia": "*type*",
-        "Windows|10|x86|Nvidia": "*type*",
-        "Windows|7|x64|Nvidia": "*type*",
-        "Mac": "*builder*",
-        "Mac|Builder": "*type*",
-        "Mac|AMD|Retina": "*type*",
-        "Mac|Intel": "*type*",
-        "Mac|Nvidia": "*type*",
-        "Linux": "*builder*",
-        "Linux|Builder": "*type*",
-        "Linux|Intel": "*type*",
-        "Linux|Nvidia": "*type*",
-        "Android": ["L32", "M64", "N64", "P32", "vk", "skgl", "skv"],
-        "Android|M64": ["QCOM"],
-    },
-)
-
-consoles.console_view(
-    name = "chromium.linux",
-    branch_selector = branches.STANDARD_MILESTONE,
-    ordering = {
-        None: ["release", "debug"],
-        "release": consoles.ordering(short_names = ["bld", "tst", "nsl", "gcc"]),
-        "cast": consoles.ordering(short_names = ["vid", "aud"]),
-    },
-)
-
-consoles.console_view(
-    name = "chromium.mac",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    ordering = {
-        None: ["release"],
-        "release": consoles.ordering(short_names = ["bld"]),
-        "debug": consoles.ordering(short_names = ["bld"]),
-        "ios|default": consoles.ordering(short_names = ["dev", "sim"]),
-    },
-)
-
-consoles.console_view(
-    name = "chromium.memory",
-    branch_selector = branches.STANDARD_MILESTONE,
-    ordering = {
-        None: ["win", "mac", "linux", "cros"],
-        "*build-or-test*": consoles.ordering(short_names = ["bld", "tst"]),
-        "linux|TSan v2": "*build-or-test*",
-        "linux|asan lsan": "*build-or-test*",
-        "linux|webkit": consoles.ordering(short_names = ["asn", "msn"]),
-    },
-)
-
-consoles.console_view(
-    name = "chromium.mojo",
-)
-
-consoles.console_view(
-    name = "chromium.packager",
-)
-
-consoles.console_view(
-    name = "chromium.rust",
-    ordering = {
-        None: [
-            "linux",
-            "android",
-        ],
-    },
-)
-
-consoles.console_view(
-    name = "chromium.swangle",
-    ordering = {
-        None: ["DEPS", "ToT ANGLE", "ToT SwiftShader"],
-        "*os*": ["Windows", "Mac"],
-        "*cpu*": consoles.ordering(short_names = ["x86", "x64"]),
-        "DEPS": "*os*",
-        "DEPS|Windows": "*cpu*",
-        "DEPS|Linux": "*cpu*",
-        "ToT ANGLE": "*os*",
-        "ToT ANGLE|Windows": "*cpu*",
-        "ToT ANGLE|Linux": "*cpu*",
-        "ToT SwiftShader": "*os*",
-        "ToT SwiftShader|Windows": "*cpu*",
-        "ToT SwiftShader|Linux": "*cpu*",
-        "Chromium": "*os*",
-    },
-)
-
-consoles.console_view(
-    name = "chromium.updater",
-)
-
-consoles.console_view(
-    name = "chromium.win",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    ordering = {
-        None: ["release", "debug"],
-        "debug|builder": consoles.ordering(short_names = ["64", "32"]),
-        "debug|tester": consoles.ordering(short_names = ["7", "10"]),
-    },
-)
-
-consoles.console_view(
-    name = "metadata.exporter",
-    header = None,
-)
-
-consoles.console_view(
-    name = "infra",
-)
-
-consoles.console_view(
-    name = "sheriff.ios",
-    title = "iOS Sheriff Console",
-    ordering = {
-        "*type*": consoles.ordering(short_names = ["dev", "sim"]),
-        None: ["chromium.mac", "chromium.fyi"],
-        "chromium.mac": "*type*",
-        "chromium.fyi|13": "*type*",
-    },
-)
-
-consoles.console_view(
-    name = "sheriff.fuchsia",
-    title = "Fuchsia Sheriff Console",
-    ordering = {
-        "*type*": consoles.ordering(short_names = ["a64", "x64"]),
-        None: ["ci", "fyi", "misc"],
-        "chromium.mac": "*type*",
-        "chromium.fyi|13": "*type*",
-    },
-)
-
-# The chromium.clang console includes some entries for builders from the chrome project
-[branches.console_view_entry(
-    builder = "chrome:ci/{}".format(name),
-    console_view = "chromium.clang",
-    category = category,
-    short_name = short_name,
-) for name, category, short_name in (
-    ("ToTLinuxOfficial", "ToT Linux", "ofi"),
-    ("ToTMacOfficial", "ToT Mac", "ofi"),
-    ("ToTWinOfficial", "ToT Windows", "ofi"),
-    ("ToTWinOfficial64", "ToT Windows|x64", "ofi"),
-    ("clang-tot-device", "iOS|internal", "dev"),
-)]
-
-# The sheriff.fuchsia console includes some entries for builders from the chrome project
-[branches.console_view_entry(
-    builder = "chrome:ci/{}".format(name),
-    console_view = "sheriff.fuchsia",
-    category = category,
-    short_name = short_name,
-) for name, category, short_name in (
-    ("fuchsia-fyi-arm64-size", "fyi", "a64-size"),
-    ("fuchsia-fyi-astro", "fyi", "astro"),
-    ("fuchsia-builder-perf-fyi", "fyi", "builder-perf"),
-    ("fuchsia-perf-fyi", "fyi", "ast-perf"),
-    ("fuchsia-perf-sherlock-fyi", "fyi", "shk-perf"),
-    ("fuchsia-x64", "ci", "x64-chrome"),
-)]
-
 # The main console includes some entries for builders from the chrome project
 [branches.console_view_entry(
     builder = "chrome:ci/{}".format(name),
@@ -467,6261 +99,51 @@
     ("win64-chrome", "win"),
 )]
 
-# The chromium.updater console includes some entries from official chrome builders.
+consoles.console_view(
+    name = "sheriff.fuchsia",
+    title = "Fuchsia Sheriff Console",
+    ordering = {
+        "*type*": consoles.ordering(short_names = ["a64", "x64"]),
+        None: ["ci", "fyi", "misc"],
+        "chromium.mac": "*type*",
+        "chromium.fyi|13": "*type*",
+    },
+)
+
+# The sheriff.fuchsia console includes some entries for builders from the chrome project
 [branches.console_view_entry(
-    builder = "chrome:official/{}".format(name),
-    console_view = "chromium.updater",
+    builder = "chrome:ci/{}".format(name),
+    console_view = "sheriff.fuchsia",
     category = category,
     short_name = short_name,
 ) for name, category, short_name in (
-    ("mac64", "official|mac", "64"),
-    ("mac-arm64", "official|mac", "arm64"),
-    ("win-asan", "official|win", "asan"),
-    ("win-clang", "official|win", "clang"),
-    ("win64-clang", "official|win", "clang (64)"),
+    ("fuchsia-fyi-arm64-size", "fyi", "a64-size"),
+    ("fuchsia-fyi-astro", "fyi", "astro"),
+    ("fuchsia-builder-perf-fyi", "fyi", "builder-perf"),
+    ("fuchsia-perf-fyi", "fyi", "ast-perf"),
+    ("fuchsia-perf-sherlock-fyi", "fyi", "shk-perf"),
+    ("fuchsia-x64", "ci", "x64-chrome"),
 )]
 
-# Builders are sorted first lexicographically by the function used to define
-# them, then lexicographically by their name
-
-ci.android_builder(
-    name = "Android ASAN (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|arm",
-        short_name = "san",
-    ),
-    # Higher build timeout since dbg ASAN builds can take a while on a clobber
-    # build.
-    execution_timeout = 4 * time.hour,
-    tree_closing = True,
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-)
-
-ci.android_builder(
-    name = "Android WebView M (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|webview",
-        short_name = "M",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "Android WebView N (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|webview",
-        short_name = "N",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "Android WebView O (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|webview",
-        short_name = "O",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "Android WebView P (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|webview",
-        short_name = "P",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "Android arm Builder (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|arm",
-        short_name = "32",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    execution_timeout = 4 * time.hour,
-    main_console_view = main_console_if_on_branch(),
-    tree_closing = True,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "Android arm64 Builder (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|arm",
-        short_name = "64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-    execution_timeout = 7 * time.hour,
-    main_console_view = main_console_if_on_branch(),
-    tree_closing = True,
-)
-
-ci.android_builder(
-    name = "Android x64 Builder (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|x86",
-        short_name = "64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    execution_timeout = 7 * time.hour,
-    main_console_view = main_console_if_on_branch(),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-)
-
-ci.android_builder(
-    name = "Android x86 Builder (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|x86",
-        short_name = "32",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    execution_timeout = 6 * time.hour,
-    main_console_view = main_console_if_on_branch(),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-)
-
-ci.android_builder(
-    name = "Cast Android (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "on_cq",
-        short_name = "cst",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    tree_closing = True,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "Deterministic Android",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|det",
-        short_name = "rel",
-    ),
-    cores = 32,
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 7 * time.hour,
-    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
-    notifies = ["Deterministic Android"],
-    tree_closing = True,
-)
-
-ci.android_builder(
-    name = "Deterministic Android (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|det",
-        short_name = "dbg",
-    ),
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-    notifies = ["Deterministic Android"],
-    tree_closing = True,
-)
-
-ci.android_builder(
-    name = "Marshmallow 64 bit Tester",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|phone",
-        short_name = "M",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "Marshmallow Tablet Tester",
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|tablet",
-        short_name = "M",
-    ),
-    # We have limited tablet capacity and thus limited ability to run
-    # tests in parallel, hence the high timeout.
-    execution_timeout = 12 * time.hour,
-    triggered_by = ["ci/Android arm Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "Nougat Phone Tester",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|phone",
-        short_name = "N",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "Oreo Phone Tester",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|phone",
-        short_name = "O",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-ci.android_builder(
-    name = "android-10-arm64-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|arm64",
-        short_name = "10",
-    ),
-)
-
-ci.android_builder(
-    name = "android-arm64-proguard-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|arm64",
-        short_name = "M proguard",
-    ),
-    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
-    execution_timeout = 6 * time.hour,
-)
-
-ci.android_builder(
-    name = "android-bfcache-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "bfcache",
-        short_name = "bfc",
-    ),
-)
-
-ci.android_builder(
-    name = "android-binary-size-generator",
-    builderless = False,
-    executable = "recipe:binary_size_generator_tot",
-    cores = 32,
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|other",
-        short_name = "size",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-    ssd = True,
-)
-
-ci.android_builder(
-    name = "android-cronet-arm-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|arm",
-        short_name = "dbg",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    notifies = ["cronet"],
-)
-
-ci.android_builder(
-    name = "android-cronet-arm-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|arm",
-        short_name = "rel",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    notifies = ["cronet"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-cronet-arm64-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|arm64",
-        short_name = "dbg",
-    ),
-    notifies = ["cronet"],
-)
-
-ci.android_builder(
-    name = "android-cronet-arm64-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|arm64",
-        short_name = "rel",
-    ),
-    notifies = ["cronet"],
-)
-
-ci.android_builder(
-    name = "android-cronet-asan-arm-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|asan",
-    ),
-    notifies = ["cronet"],
-)
-
-ci.android_builder(
-    name = "android-cronet-arm-rel-kitkat-tests",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test",
-        short_name = "k",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    notifies = ["cronet"],
-    triggered_by = ["ci/android-cronet-arm-rel"],
-)
-
-ci.android_builder(
-    name = "android-cronet-arm-rel-lollipop-tests",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test",
-        short_name = "l",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    notifies = ["cronet"],
-    triggered_by = ["ci/android-cronet-arm-rel"],
-)
-
-# Runs on a specific machine with an attached phone
-ci.android_builder(
-    name = "android-cronet-marshmallow-arm64-perf-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test|perf",
-        short_name = "m",
-    ),
-    cores = None,
-    cpu = None,
-    executable = "recipe:cronet",
-    notifies = ["cronet"],
-    os = os.ANDROID,
-)
-
-ci.android_builder(
-    name = "android-cronet-arm64-rel-marshmallow-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test",
-        short_name = "m",
-    ),
-    notifies = ["cronet"],
-    triggered_by = ["ci/android-cronet-arm64-rel"],
-)
-
-ci.android_builder(
-    name = "android-cronet-x86-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|x86",
-        short_name = "dbg",
-    ),
-    notifies = ["cronet"],
-)
-
-ci.android_builder(
-    name = "android-cronet-x86-dbg-oreo-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test",
-        short_name = "o",
-    ),
-    notifies = ["cronet"],
-    triggered_by = ["ci/android-cronet-x86-dbg"],
-)
-
-ci.android_builder(
-    name = "android-cronet-x86-dbg-pie-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test",
-        short_name = "p",
-    ),
-    notifies = ["cronet"],
-    triggered_by = ["ci/android-cronet-x86-dbg"],
-)
-
-ci.android_builder(
-    name = "android-cronet-x86-dbg-10-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test",
-        short_name = "10",
-    ),
-    notifies = ["cronet"],
-    triggered_by = ["ci/android-cronet-x86-dbg"],
-)
-
-ci.android_builder(
-    name = "android-cronet-x86-dbg-11-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|test",
-        short_name = "11",
-    ),
-    notifies = ["cronet"],
-    triggered_by = ["ci/android-cronet-x86-dbg"],
-)
-
-ci.android_builder(
-    name = "android-cronet-x86-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet|x86",
-        short_name = "rel",
-    ),
-    notifies = ["cronet"],
-)
-
-ci.android_builder(
-    name = "android-incremental-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|incremental",
-    ),
-)
-
-ci.android_builder(
-    name = "android-marshmallow-arm64-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "on_cq",
-        short_name = "M",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    execution_timeout = 4 * time.hour,
-    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
-    main_console_view = main_console_if_on_branch(),
-    tree_closing = True,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-marshmallow-x86-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "on_cq|x86",
-        short_name = "M",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    tree_closing = True,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-marshmallow-x86-rel-non-cq",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|x86",
-        short_name = "M_non-cq",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-pie-arm64-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|phone",
-        short_name = "P",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Android arm64 Builder (dbg)"],
-)
-
-# TODO(crbug/1182468) Remove android coverage bots after coverage is
-# running on CQ.
-ci.android_builder(
-    name = "android-pie-arm64-coverage-experimental-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|arm64",
-        short_name = "p-cov",
-    ),
-)
-
-ci.android_builder(
-    name = "android-pie-arm64-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "on_cq",
-        short_name = "P",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    execution_timeout = 4 * time.hour,
-    main_console_view = main_console_if_on_branch(),
-    tree_closing = True,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-11-x86-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|x86",
-        short_name = "11",
-    ),
-    tree_closing = True,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-weblayer-marshmallow-x86-rel-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|weblayer",
-        short_name = "M",
-    ),
-    triggered_by = ["android-weblayer-with-aosp-webview-x86-rel"],
-    notifies = ["weblayer-sheriff"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-weblayer-oreo-x86-rel-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|weblayer",
-        short_name = "O",
-    ),
-    triggered_by = ["android-weblayer-x86-rel"],
-    notifies = ["weblayer-sheriff"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-weblayer-pie-x86-rel-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|weblayer",
-        short_name = "P",
-    ),
-    triggered_by = ["android-weblayer-x86-rel"],
-    notifies = ["weblayer-sheriff"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-weblayer-with-aosp-webview-x86-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|weblayer_with_aosp_webview",
-        short_name = "x86",
-    ),
-)
-
-ci.android_builder(
-    name = "android-weblayer-x86-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|weblayer",
-        short_name = "x86",
-    ),
-)
-
-ci.android_fyi_builder(
-    name = "Android ASAN (dbg) (reclient)",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|arm",
-        short_name = "san",
-    ),
-    # Higher build timeout since dbg ASAN builds can take a while on a clobber
-    # build.
-    execution_timeout = 4 * time.hour,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = 150,
-    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-    schedule = "triggered",  # triggered manually via Scheduler UI
-)
-
-ci.android_fyi_builder(
-    name = "android-pie-arm64-wpt-rel-non-cq",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|arm64",
-        short_name = "P-WPT",
-    ),
-)
-
-ci.android_fyi_builder(
-    name = "android-web-platform-pie-x86-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|web-platform",
-        short_name = "P",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_fyi_builder(
-    name = "android-weblayer-pie-x86-wpt-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|weblayer",
-        short_name = "P",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_fyi_builder(
-    name = "android-weblayer-pie-x86-wpt-smoketest",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|weblayer",
-        short_name = "P",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_fyi_builder(
-    name = "android-webview-pie-x86-wpt-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|webview",
-        short_name = "P",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_builder(
-    name = "android-pie-x86-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder_tester|x86",
-        short_name = "P",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_fyi_builder(
-    name = "android-weblayer-10-x86-rel-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|weblayer",
-        short_name = "10",
-    ),
-    triggered_by = ["android-weblayer-with-aosp-webview-x86-fyi-rel"],
-    notifies = ["weblayer-sheriff"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_fyi_builder(
-    name = "android-weblayer-with-aosp-webview-x86-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|weblayer_with_aosp_webview",
-        short_name = "x86",
-    ),
-)
-
-ci.android_fyi_builder(
-    name = "Android WebView P FYI (rel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "webview",
-        short_name = "p-rel",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-# TODO(crbug.com/1022533#c40): Remove this builder once there are no associated
-# disabled tests.
-ci.android_fyi_builder(
-    name = "android-pie-x86-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "emulator|P|x86",
-        short_name = "rel",
-    ),
-    goma_jobs = goma.jobs.J150,
-    # Set to an empty list to avoid chromium-gitiles-trigger triggering new
-    # builds. Also we don't set any `schedule` since this builder is for
-    # reference only and should not run any new builds.
-    triggered_by = [],
-)
-
-ci.android_fyi_builder(
-    name = "android-10-x86-fyi-rel-tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "tester|10",
-        short_name = "10",
-    ),
-    triggered_by = ["android-x86-fyi-rel"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-# TODO(crbug.com/1137474): Remove this builder once there are no associated
-# disabled tests.
-ci.android_fyi_builder(
-    name = "android-11-x86-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "emulator|11|x86",
-        short_name = "rel",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-    # Set to an empty list to avoid chromium-gitiles-trigger triggering new
-    # builds. Also we don't set any `schedule` since this builder is for
-    # reference only and should not run any new builds.
-    triggered_by = [],
-)
-
-ci.android_fyi_builder(
-    name = "android-12-x64-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "emulator|12|x64",
-        short_name = "rel",
-    ),
-    # Bump to 6h for now since compile on x64 seems slower than x86. It could
-    # take 3h on Android-12 (For example ci.chromium.org/b/8841892751541698720)
-    # vs 1h on Android-11 (For example ci.chromium.org/b/8841899947736889024)
-    # TODO(crbug.com/1229245): Look into ways to improve the compile time.
-    execution_timeout = 6 * time.hour,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.android_fyi_builder(
-    name = "android-annotator-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "network|traffic|annotations",
-        short_name = "and",
-    ),
-    notifies = ["annotator-rel"],
-)
-
-ci.angle_linux_builder(
-    name = "android-angle-arm64-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|Builder|ANGLE",
-        short_name = "arm64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "android-angle-arm64-nexus5x",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|Nexus5X|ANGLE",
-        short_name = "arm64",
-    ),
-    triggered_by = ["android-angle-arm64-builder"],
-)
-
-ci.angle_linux_builder(
-    name = "android-angle-chromium-arm64-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|Builder|Chromium",
-        short_name = "arm64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "android-angle-chromium-arm64-nexus5x",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|Nexus5X|Chromium",
-        short_name = "arm64",
-    ),
-    triggered_by = ["android-angle-chromium-arm64-builder"],
-)
-
-ci.android_fyi_builder(
-    name = "android-x86-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "builder|x86",
-        short_name = "x86",
-    ),
-)
-
-ci.angle_linux_builder(
-    name = "fuchsia-angle-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Fuchsia|Builder|ANGLE",
-        short_name = "x64",
-    ),
-)
-
-ci.angle_linux_builder(
-    name = "linux-angle-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Builder|ANGLE",
-        short_name = "x64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "linux-angle-intel",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Intel|ANGLE",
-        short_name = "x64",
-    ),
-    triggered_by = ["linux-angle-builder"],
-)
-
-ci.angle_thin_tester(
-    name = "linux-angle-nvidia",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|NVIDIA|ANGLE",
-        short_name = "x64",
-    ),
-    triggered_by = ["linux-angle-builder"],
-)
-
-ci.angle_linux_builder(
-    name = "linux-angle-chromium-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Builder|Chromium",
-        short_name = "x64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "linux-angle-chromium-intel",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Intel|Chromium",
-        short_name = "x64",
-    ),
-    triggered_by = ["linux-angle-chromium-builder"],
-)
-
-ci.angle_thin_tester(
-    name = "linux-angle-chromium-nvidia",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|NVIDIA|Chromium",
-        short_name = "x64",
-    ),
-    triggered_by = ["linux-angle-chromium-builder"],
-)
-
-ci.angle_mac_builder(
-    name = "mac-angle-chromium-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Builder|Chromium",
-        short_name = "x64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "mac-angle-chromium-amd",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|AMD|Chromium",
-        short_name = "x64",
-    ),
-    triggered_by = ["mac-angle-chromium-builder"],
-)
-
-ci.angle_thin_tester(
-    name = "mac-angle-chromium-intel",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Intel|Chromium",
-        short_name = "x64",
-    ),
-    triggered_by = ["mac-angle-chromium-builder"],
-)
-
-ci.angle_mac_builder(
-    name = "ios-angle-builder",
-    xcode = xcode.x12d4e,
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|Builder|ANGLE",
-        short_name = "x64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "ios-angle-intel",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|Intel|ANGLE",
-        short_name = "x64",
-    ),
-    triggered_by = ["ios-angle-builder"],
-)
-
-ci.angle_windows_builder(
-    name = "win-angle-chromium-x64-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|Chromium",
-        short_name = "x64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "win10-angle-chromium-x64-intel",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Intel|Chromium",
-        short_name = "x64",
-    ),
-    triggered_by = ["win-angle-chromium-x64-builder"],
-)
-
-ci.angle_thin_tester(
-    name = "win10-angle-chromium-x64-nvidia",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|NVIDIA|Chromium",
-        short_name = "x64",
-    ),
-    triggered_by = ["win-angle-chromium-x64-builder"],
-)
-
-ci.angle_windows_builder(
-    name = "win-angle-chromium-x86-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|Chromium",
-        short_name = "x86",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "win7-angle-chromium-x86-amd",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Win7-AMD|Chromium",
-        short_name = "x86",
-    ),
-    triggered_by = ["win-angle-chromium-x86-builder"],
-)
-
-ci.angle_windows_builder(
-    name = "win-angle-x64-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|ANGLE",
-        short_name = "x64",
-    ),
-)
-
-ci.angle_thin_tester(
-    name = "win7-angle-x64-nvidia",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Win7-NVIDIA|ANGLE",
-        short_name = "x64",
-    ),
-    triggered_by = ["win-angle-x64-builder"],
-)
-
-ci.angle_thin_tester(
-    name = "win10-angle-x64-intel",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Intel|ANGLE",
-        short_name = "x64",
-    ),
-    triggered_by = ["win-angle-x64-builder"],
-)
-
-ci.angle_thin_tester(
-    name = "win10-angle-x64-nvidia",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|NVIDIA|ANGLE",
-        short_name = "x64",
-    ),
-    triggered_by = ["win-angle-x64-builder"],
-)
-
-ci.chromium_builder(
-    name = "android-archive-dbg",
-    # Bump to 32 if needed.
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "dbg",
-    ),
-    execution_timeout = 4 * time.hour,
-    cores = 8,
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.chromium_builder(
-    name = "android-archive-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "rel",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "android-archive-rel.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "android-official",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = False,
-    main_console_view = "main",
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "off",
-    ),
-    cores = 32,
-    os = os.LINUX_BIONIC_REMOVE,
-    tree_closing = False,
-
-    # See https://crbug.com/1153349#c22, as we update symbol_level=2, build
-    # needs longer time to complete.
-    execution_timeout = 7 * time.hour,
-)
-
-ci.chromium_builder(
-    name = "fuchsia-official",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = False,
-    main_console_view = "main",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia",
-            short_name = "off",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "ci",
-            short_name = "off-x64",
-        ),
-    ],
-    cores = 32,
-    # TODO: Change this back down to something reasonable once these builders
-    # have populated their cached by getting through the compile step
-    execution_timeout = 10 * time.hour,
-    os = os.LINUX_BIONIC_REMOVE,
-    tree_closing = False,
-)
-
-ci.chromium_builder(
-    name = "linux-archive-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "dbg",
-    ),
-    # Bump to 32 if needed.
-    cores = 8,
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.chromium_builder(
-    name = "linux-archive-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "rel",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    notifies = ["linux-archive-rel"],
-    os = os.LINUX_BIONIC_REMOVE,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "linux-archive-rel.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "linux-archive-tagged",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "tag",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-    tree_closing = False,
-    schedule = "triggered",
-    triggered_by = [],
-    execution_timeout = 7 * time.hour,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "archive_datas": [
-                {
-                    "files": [
-                        "chrome",
-                        "chrome-wrapper",
-                        "chrome_100_percent.pak",
-                        "chrome_200_percent.pak",
-                        "chrome_crashpad_handler",
-                        "chrome_sandbox",
-                        "icudtl.dat",
-                        "libEGL.so",
-                        "libGLESv2.so",
-                        "libvk_swiftshader.so",
-                        "libvulkan.so.1",
-                        "MEIPreload/manifest.json",
-                        "MEIPreload/preloaded_data.pb",
-                        "nacl_helper",
-                        "nacl_helper_bootstrap",
-                        "nacl_irt_x86_64.nexe",
-                        "product_logo_48.png",
-                        "resources.pak",
-                        "swiftshader/libGLESv2.so",
-                        "swiftshader/libEGL.so",
-                        "v8_context_snapshot.bin",
-                        "vk_swiftshader_icd.json",
-                        "xdg-mime",
-                        "xdg-settings",
-                    ],
-                    "dirs": ["ClearKeyCdm", "locales", "resources"],
-                    "gcs_bucket": "chromium-browser-versioned",
-                    "gcs_path": "experimental/Linux_x64_Tagged/{%chromium_version%}/chrome-linux.zip",
-                    "archive_type": "ARCHIVE_TYPE_ZIP",
-                },
-                {
-                    "files": [
-                        "chromedriver",
-                    ],
-                    "gcs_bucket": "chromium-browser-versioned",
-                    "gcs_path": "experimental/Linux_x64_Tagged/{%chromium_version%}/chromedriver_linux64.zip",
-                    "archive_type": "ARCHIVE_TYPE_ZIP",
-                },
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "linux-official",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "off",
-    ),
-    cores = 32,
-    execution_timeout = 7 * time.hour,
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-    tree_closing = False,
-)
-
-ci.chromium_builder(
-    name = "mac-archive-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "dbg",
-    ),
-    # Bump to 8 cores if needed.
-    cores = 4,
-    main_console_view = "main",
-    os = os.MAC_DEFAULT,
-)
-
-ci.chromium_builder(
-    name = "mac-archive-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "rel",
-    ),
-    cores = 12,
-    main_console_view = "main",
-    os = os.MAC_DEFAULT,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "mac-archive-rel.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "mac-archive-tagged",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "tag",
-    ),
-    main_console_view = "main",
-    cores = 12,
-    os = os.MAC_DEFAULT,
-    tree_closing = False,
-    schedule = "triggered",
-    triggered_by = [],
-    execution_timeout = 7 * time.hour,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "mac-tagged.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "mac-arm64-archive-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac|arm",
-        short_name = "dbg",
-    ),
-    cores = 12,
-    main_console_view = "main",
-    os = os.MAC_DEFAULT,
-)
-
-ci.chromium_builder(
-    name = "mac-arm64-archive-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac|arm",
-        short_name = "rel",
-    ),
-    main_console_view = "main",
-    cores = 12,
-    os = os.MAC_DEFAULT,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "mac-arm64-archive-rel.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "mac-arm64-archive-tagged",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac|arm",
-        short_name = "tag",
-    ),
-    main_console_view = "main",
-    cores = 12,
-    os = os.MAC_DEFAULT,
-    tree_closing = False,
-    schedule = "triggered",
-    triggered_by = [],
-    execution_timeout = 7 * time.hour,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "mac-tagged.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "mac-official",
-    builderless = False,
-    # TODO(https://crbug.com/1072012) Use the default console view and add
-    # main_console_view = 'main' once the build is green
-    console_view_entry = consoles.console_view_entry(
-        console_view = "chromium.fyi",
-        category = "mac",
-        short_name = "off",
-    ),
-    # TODO(crbug.com/1279290):
-    # builds with LTO change take long time.
-    # Keep in sync with mac-official in try.star.
-    execution_timeout = 30 * time.hour,
-    main_console_view = main_console_if_on_branch(),
-    tree_closing = False,
-    os = os.MAC_ANY,
-    cores = None,  # TODO(thakis): Bump this up.
-)
-
-ci.chromium_builder(
-    name = "win-archive-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|dbg",
-        short_name = "64",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.WINDOWS_DEFAULT,
-    tree_closing = False,
-)
-
-ci.chromium_builder(
-    name = "win-archive-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|rel",
-        short_name = "64",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.WINDOWS_DEFAULT,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "win-archive-rel.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "win-archive-tagged",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|tag",
-        short_name = "64",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.WINDOWS_DEFAULT,
-    tree_closing = False,
-    schedule = "triggered",
-    triggered_by = [],
-    execution_timeout = 7 * time.hour,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "win-tagged.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "win-official",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    main_console_view = "main",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|off",
-        short_name = "64",
-    ),
-    cores = 32,
-    os = os.WINDOWS_DEFAULT,
-    # TODO(crbug.com/1155416):
-    # builds with PGO change take long time.
-    execution_timeout = 7 * time.hour,
-    tree_closing = False,
-)
-
-ci.chromium_builder(
-    name = "win32-archive-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|dbg",
-        short_name = "32",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.WINDOWS_DEFAULT,
-    tree_closing = False,
-)
-
-ci.chromium_builder(
-    name = "win32-archive-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|rel",
-        short_name = "32",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.WINDOWS_DEFAULT,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "win32-archive-rel.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "win32-archive-tagged",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|tag",
-        short_name = "32",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.WINDOWS_DEFAULT,
-    tree_closing = False,
-    schedule = "triggered",
-    triggered_by = [],
-    execution_timeout = 7 * time.hour,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "win-tagged.json",
-            ],
-        },
-    },
-)
-
-ci.chromium_builder(
-    name = "win32-official",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    main_console_view = "main",
-    console_view_entry = consoles.console_view_entry(
-        category = "win|off",
-        short_name = "32",
-    ),
-    cores = 32,
-    os = os.WINDOWS_DEFAULT,
-    # TODO(crbug.com/1155416):
-    # builds with PGO change take long time.
-    execution_timeout = 7 * time.hour,
-    tree_closing = False,
-)
-
-ci.chromiumos_builder(
-    name = "linux-ash-chromium-generator-rel",
-    # This builder gets triggered against multiple branches, so it shouldn't be
-    # bootstrapped
-    bootstrap = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-    ),
-    tree_closing = False,
-    main_console_view = "main",
-    notifies = ["chrome-lacros-engprod-alerts"],
-    triggered_by = [],
-    schedule = "triggered",
-    sheriff_rotations = None,
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "cipd_archive_datas": [
-                {
-                    "yaml_files": [
-                        "gen_linux_ash_chromium_cipd_yaml_cipd.yaml",
-                    ],
-                    "refs": [
-                        "{%channel%}",
-                    ],
-                    "tags": {
-                        "version": "{%chromium_version%}",
-                    },
-                    # Because we don't run any tests.
-                    "only_set_refs_on_tests_success": False,
-                },
-            ],
-        },
-    },
-)
-
-ci.chromiumos_builder(
-    name = "Linux ChromiumOS Full",
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-        short_name = "ful",
-    ),
-    main_console_view = "main",
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "source_side_spec_path": [
-                "src",
-                "infra",
-                "archive_config",
-                "linux-chromiumos-full.json",
-            ],
-        },
-    },
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-amd64-generic-asan-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|release|x64",
-        short_name = "asn",
-    ),
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-amd64-generic-cfi-thin-lto-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|release|x64",
-        short_name = "cfi",
-    ),
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-amd64-generic-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|debug|x64",
-        short_name = "dbg",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-amd64-generic-lacros-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros|x64",
-        short_name = "dbg",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-amd64-generic-rel",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|release|x64",
-        short_name = "rel",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-arm-generic-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|debug",
-        short_name = "arm",
-    ),
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-arm-generic-rel",
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_arch = builder_config.target_arch.ARM,
-            target_bits = 32,
-            target_platform = builder_config.target_platform.CHROMEOS,
-            target_cros_boards = ["arm-generic"],
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["chromeos"],
-        ),
-    ),
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|release",
-        short_name = "arm",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "chromeos-kevin-rel",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|release",
-        short_name = "kvn",
-    ),
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "linux-chromeos-annotator-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "rel",
-    ),
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "lacros-amd64-generic-binary-size-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros|size",
-    ),
-    main_console_view = "main",
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "archive_datas": [
-                # The list of files and dirs should be synched with
-                # _TRACKED_ITEMS in //build/lacros/lacros_resource_sizes.py.
-                {
-                    "files": [
-                        "chrome",
-                        "chrome_100_percent.pak",
-                        "chrome_200_percent.pak",
-                        "chrome_crashpad_handler",
-                        "headless_lib.pak",
-                        "icudtl.dat",
-                        "nacl_helper",
-                        "nacl_irt_x86_64.nexe",
-                        "resources.pak",
-                        "snapshot_blob.bin",
-                    ],
-                    "dirs": ["locales", "swiftshader"],
-                    "gcs_bucket": "chromium-lacros-fishfood",
-                    "gcs_path": "x86_64/{%position%}/lacros.zip",
-                    "archive_type": "ARCHIVE_TYPE_ZIP",
-                },
-            ],
-        },
-    },
-)
-
-ci.chromiumos_builder(
-    name = "lacros-amd64-generic-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros|x64",
-        short_name = "rel",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.chromiumos_builder(
-    name = "lacros-arm-generic-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros|arm",
-        short_name = "arm",
-    ),
-    # TODO(crbug.com/1202631) Enable tree closing when stable.
-    tree_closing = False,
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "linux-chromeos-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-        short_name = "dbg",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.chromiumos_builder(
-    name = "linux-chromeos-rel",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-        short_name = "rel",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.chromiumos_builder(
-    name = "linux-lacros-builder-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-        short_name = "lcr",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.chromiumos_builder(
-    name = "linux-lacros-tester-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-        short_name = "lcr",
-    ),
-    main_console_view = "main",
-    cq_mirrors_console_view = "mirrors",
-    triggered_by = ["linux-lacros-builder-rel"],
-    tree_closing = False,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.chromiumos_builder(
-    name = "linux-lacros-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug",
-        short_name = "lcr",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-# For Chromebox for meetings(CfM)
-ci.chromiumos_builder(
-    name = "linux-cfm-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "simple|release",
-        short_name = "cfm",
-    ),
-    main_console_view = "main",
-)
-
-ci.cipd_3pp_builder(
-    name = "3pp-linux-amd64-packager",
-    os = os.LINUX_DEFAULT,
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "3pp|linux",
-        short_name = "amd64",
-    ),
-    notifies = ["chromium-3pp-packager"],
-    schedule = "with 6h interval",
-    triggered_by = [],
-    properties = {
-        "$build/chromium_3pp": {
-            "platform": "linux-amd64",
-            "preprocess": [{
-                "name": "third_party/android_deps",
-                "cmd": [
-                    "{CHECKOUT}/src/third_party/android_deps/fetch_all.py",
-                    "-v",
-                    "--ignore-vulnerabilities",
-                ],
-            }],
-            "gclient_config": "chromium",
-            "gclient_apply_config": ["android"],
-        },
-    },
-)
-
-ci.cipd_3pp_builder(
-    name = "3pp-mac-amd64-packager",
-    os = os.MAC_DEFAULT,
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "3pp|mac",
-        short_name = "amd64",
-    ),
-    cores = None,
-    notifies = ["chromium-3pp-packager"],
-    # TODO(crbug.com/1267449): Trigger builds routinely once works fine.
-    schedule = "triggered",
-    triggered_by = [],
-    properties = {
-        "$build/chromium_3pp": {
-            "platform": "mac-amd64",
-            "gclient_config": "chromium",
-        },
-    },
-)
-
-ci.cipd_builder(
-    name = "android-androidx-packager",
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "androidx",
-    ),
-    notifies = ["chromium-androidx-packager"],
-    executable = "recipe:android/androidx_packager",
-    schedule = "0 7,14,22 * * * *",
-    sheriff_rotations = sheriff_rotations.ANDROID,
-    triggered_by = [],
-)
-
-ci.cipd_builder(
-    name = "android-avd-packager",
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "avd",
-    ),
-    executable = "recipe:android/avd_packager",
-    # Triggered manually through the scheduler UI
-    # https://luci-scheduler.appspot.com/jobs/chromium/android-avd-packager
-    schedule = "triggered",
-    triggered_by = [],
-    os = os.LINUX_BIONIC_REMOVE,
-    properties = {
-        "avd_configs": [
-            "tools/android/avd/proto/creation/generic_android23.textpb",
-            "tools/android/avd/proto/creation/generic_android27.textpb",
-            "tools/android/avd/proto/creation/generic_android28.textpb",
-            "tools/android/avd/proto/creation/generic_android29.textpb",
-            "tools/android/avd/proto/creation/generic_android30.textpb",
-            "tools/android/avd/proto/creation/generic_android31.textpb",
-            "tools/android/avd/proto/creation/generic_playstore_android27.textpb",
-            "tools/android/avd/proto/creation/generic_playstore_android28.textpb",
-            "tools/android/avd/proto/creation/generic_playstore_android30.textpb",
-            "tools/android/avd/proto/creation/generic_playstore_android31.textpb",
-        ],
-    },
-)
-
-ci.cipd_builder(
-    name = "android-sdk-packager",
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "sdk",
-    ),
-    executable = "recipe:android/sdk_packager",
-    schedule = "0 7 * * *",
-    triggered_by = [],
-    properties = {
-        # We still package part of build-tools;25.0.2 to support
-        # http://bit.ly/2KNUygZ
-        "packages": [
-            {
-                "sdk_package_name": "build-tools;25.0.2",
-                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/25.0.2.yaml",
-            },
-            {
-                "sdk_package_name": "build-tools;29.0.2",
-                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/29.0.2.yaml",
-            },
-            {
-                "sdk_package_name": "build-tools;30.0.1",
-                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/30.0.1.yaml",
-            },
-            {
-                "sdk_package_name": "build-tools;31.0.0",
-                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/31.0.0.yaml",
-            },
-            {
-                "sdk_package_name": "cmdline-tools;latest",
-                "cipd_yaml": "third_party/android_sdk/cipd/cmdline-tools.yaml",
-            },
-            {
-                "sdk_package_name": "emulator",
-                "cipd_yaml": "third_party/android_sdk/cipd/emulator.yaml",
-            },
-            {
-                "sdk_package_name": "patcher;v4",
-                "cipd_yaml": "third_party/android_sdk/cipd/patcher/v4.yaml",
-            },
-            {
-                "sdk_package_name": "platforms;android-29",
-                "cipd_yaml": "third_party/android_sdk/cipd/platforms/android-29.yaml",
-            },
-            {
-                "sdk_package_name": "platforms;android-30",
-                "cipd_yaml": "third_party/android_sdk/cipd/platforms/android-30.yaml",
-            },
-            {
-                "sdk_package_name": "platforms;android-31",
-                "cipd_yaml": "third_party/android_sdk/cipd/platforms/android-31.yaml",
-            },
-            {
-                "sdk_package_name": "platform-tools",
-                "cipd_yaml": "third_party/android_sdk/cipd/platform-tools.yaml",
-            },
-            {
-                "sdk_package_name": "sources;android-29",
-                "cipd_yaml": "third_party/android_sdk/cipd/sources/android-29.yaml",
-            },
-            {
-                "sdk_package_name": "sources;android-30",
-                "cipd_yaml": "third_party/android_sdk/cipd/sources/android-30.yaml",
-            },
-            {
-                "sdk_package_name": "sources;android-31",
-                "cipd_yaml": "third_party/android_sdk/cipd/sources/android-31.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-23;google_apis;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-23/google_apis/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-27;google_apis;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-27/google_apis/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-27;google_apis_playstore;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-27/google_apis_playstore/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-28;google_apis;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-28/google_apis/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-28;google_apis_playstore;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-28/google_apis_playstore/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-29;google_apis;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-29/google_apis/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-29;google_apis_playstore;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-29/google_apis_playstore/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-30;google_apis;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-30/google_apis/x86.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-30;google_apis_playstore;x86",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-30/google_apis_playstore/x86.yaml",
-            },
-            # use x86_64 since sdkmanager don't ship x86 for android-31 and above.
-            {
-                "sdk_package_name": "system-images;android-31;google_apis;x86_64",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-31/google_apis/x86_64.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-31;google_apis_playstore;x86_64",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-31/google_apis_playstore/x86_64.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-32;google_apis;x86_64",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-32/google_apis/x86_64.yaml",
-            },
-            {
-                "sdk_package_name": "system-images;android-32;google_apis_playstore;x86_64",
-                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-32/google_apis_playstore/x86_64.yaml",
-            },
-        ],
-    },
-)
-
-ci.clang_builder(
-    name = "CFI Linux CF",
-    goma_backend = goma.backend.RBE_PROD,
-    console_view_entry = consoles.console_view_entry(
-        category = "CFI|Linux",
-        short_name = "CF",
-    ),
-    notifies = ["CFI Linux"],
-)
-
-ci.clang_builder(
-    name = "CFI Linux ToT",
-    console_view_entry = consoles.console_view_entry(
-        category = "CFI|Linux",
-        short_name = "ToT",
-    ),
-    notifies = ["CFI Linux"],
-)
-
-ci.clang_builder(
-    name = "CrWinAsan",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows|Asan",
-        short_name = "asn",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "CrWinAsan(dll)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows|Asan",
-        short_name = "dll",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTAndroid",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Android",
-        short_name = "rel",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTAndroid (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Android",
-        short_name = "dbg",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTAndroid x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Android",
-        short_name = "x64",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTAndroid x86",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Android",
-        short_name = "x86",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTAndroidCoverage x86",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Code Coverage",
-        short_name = "and",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.clang_builder(
-    name = "ToTAndroid64",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Android",
-        short_name = "a64",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTAndroidASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Android",
-        short_name = "asn",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTAndroidOfficial",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Android",
-        short_name = "off",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTChromeOS",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT ChromeOS",
-        short_name = "rel",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTChromeOS (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT ChromeOS",
-        short_name = "dbg",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTFuchsia x64",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "ToT Fuchsia",
-            short_name = "x64",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "misc",
-            short_name = "clang-x64",
-        ),
-    ],
-)
-
-ci.clang_builder(
-    name = "ToTFuchsiaOfficial",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "ToT Fuchsia",
-            short_name = "off",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "misc",
-            short_name = "clang-off",
-        ),
-    ],
-)
-
-def clang_tot_linux_builder(short_name, category = "ToT Linux", **kwargs):
-    ci.clang_builder(
-        console_view_entry = consoles.console_view_entry(
-            category = category,
-            short_name = short_name,
-        ),
-        notifies = [luci.notifier(
-            name = "ToT Linux notifier",
-            on_new_status = ["FAILURE"],
-            notify_emails = ["thomasanderson@chromium.org"],
-        )],
-        **kwargs
-    )
-
-clang_tot_linux_builder(
-    name = "ToTLinux",
-    short_name = "rel",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinux (dbg)",
-    short_name = "dbg",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinuxASan",
-    short_name = "asn",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinuxASanLibfuzzer",
-    # Requires a large disk, so has a machine specifically devoted to it
-    builderless = False,
-    short_name = "fuz",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinuxCoverage",
-    category = "ToT Code Coverage",
-    short_name = "linux",
-    executable = "recipe:chromium_clang_coverage_tot",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinuxMSan",
-    short_name = "msn",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinuxPGO",
-    short_name = "pgo",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinuxTSan",
-    short_name = "tsn",
-)
-
-clang_tot_linux_builder(
-    name = "ToTLinuxUBSanVptr",
-    short_name = "usn",
-)
-
-ci.clang_builder(
-    name = "ToTWin",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows",
-        short_name = "rel",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWin(dbg)",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows",
-        short_name = "dbg",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWin(dll)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows",
-        short_name = "dll",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWin64",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows|x64",
-        short_name = "rel",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWin64(dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows|x64",
-        short_name = "dbg",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWin64(dll)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows|x64",
-        short_name = "dll",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWinASanLibfuzzer",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows|Asan",
-        short_name = "fuz",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWinCFI",
-    console_view_entry = consoles.console_view_entry(
-        category = "CFI|Win",
-        short_name = "x86",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWinCFI64",
-    console_view_entry = consoles.console_view_entry(
-        category = "CFI|Win",
-        short_name = "x64",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWindowsCoverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Code Coverage",
-        short_name = "win",
-    ),
-    executable = "recipe:chromium_clang_coverage_tot",
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "ToTWin64PGO",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows|x64",
-        short_name = "pgo",
-    ),
-    os = os.WINDOWS_ANY,
-)
-
-ci.clang_builder(
-    name = "linux-win_cross-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Windows",
-        short_name = "lxw",
-    ),
-)
-
-ci.clang_builder(
-    name = "ToTiOS",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|public",
-        short_name = "sim",
-    ),
-    cores = None,
-    os = os.MAC_11,
-    ssd = True,
-    xcode = xcode.x13main,
-)
-
-ci.clang_builder(
-    name = "ToTiOSDevice",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|public",
-        short_name = "dev",
-    ),
-    cores = None,
-    os = os.MAC_11,
-    ssd = True,
-    xcode = xcode.x13main,
-)
-
-ci.clang_mac_builder(
-    name = "ToTMac",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Mac",
-        short_name = "rel",
-    ),
-    cores = None,
-)
-
-ci.clang_mac_builder(
-    name = "ToTMac (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Mac",
-        short_name = "dbg",
-    ),
-    cores = None,
-)
-
-ci.clang_mac_builder(
-    name = "ToTMacASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Mac",
-        short_name = "asn",
-    ),
-    cores = None,
-)
-
-ci.clang_mac_builder(
-    name = "ToTMacCoverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT Code Coverage",
-        short_name = "mac",
-    ),
-    executable = "recipe:chromium_clang_coverage_tot",
-    cores = None,
-)
-
-ci.dawn_linux_builder(
-    name = "Dawn Linux x64 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Linux|Builder",
-        short_name = "x64",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.dawn_linux_builder(
-    name = "Dawn Linux x64 DEPS Builder",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Linux|Builder",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Linux x64 DEPS Release (Intel HD 630)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Linux|Intel",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Linux x64 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Linux x64 DEPS Release (NVIDIA)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Linux|Nvidia",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Linux x64 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Linux x64 Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Linux|Intel",
-        short_name = "x64",
-    ),
-    triggered_by = ["Dawn Linux x64 Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Linux x64 Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Linux|Nvidia",
-        short_name = "x64",
-    ),
-    triggered_by = ["Dawn Linux x64 Builder"],
-)
-
-ci.dawn_mac_builder(
-    name = "Dawn Mac x64 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Mac|Builder",
-        short_name = "x64",
-    ),
-)
-
-ci.dawn_mac_builder(
-    name = "Dawn Mac x64 DEPS Builder",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Mac|Builder",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-)
-
-# Note that the Mac testers are all thin Linux VMs, triggering jobs on the
-# physical Mac hardware in the Swarming pool which is why they run on linux
-ci.dawn_thin_tester(
-    name = "Dawn Mac x64 DEPS Release (AMD)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Mac|AMD",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Mac x64 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Mac x64 DEPS Release (Intel)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Mac|Intel",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Mac x64 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Mac x64 Experimental Release (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Mac|AMD",
-        short_name = "exp",
-    ),
-    triggered_by = ["Dawn Mac x64 Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Mac x64 Experimental Release (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Mac|Intel",
-        short_name = "exp",
-    ),
-    triggered_by = ["Dawn Mac x64 Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Mac x64 Release (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Mac|AMD",
-        short_name = "x64",
-    ),
-    triggered_by = ["Dawn Mac x64 Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Mac x64 Release (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Mac|Intel",
-        short_name = "x64",
-    ),
-    triggered_by = ["Dawn Mac x64 Builder"],
-)
-
-ci.dawn_windows_builder(
-    name = "Dawn Win10 x64 ASAN Release",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Windows|ASAN",
-        short_name = "x64",
-    ),
-)
-
-ci.dawn_windows_builder(
-    name = "Dawn Win10 x64 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Windows|Builder",
-        short_name = "x64",
-    ),
-)
-
-ci.dawn_windows_builder(
-    name = "Dawn Win10 x64 DEPS Builder",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows|Builder",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-)
-
-# Note that the Win testers are all thin Linux VMs, triggering jobs on the
-# physical Win hardware in the Swarming pool, which is why they run on linux
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x64 DEPS Release (Intel HD 630)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows|Intel",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Win10 x64 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x64 DEPS Release (NVIDIA)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows|Nvidia",
-        short_name = "x64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Win10 x64 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x64 Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Windows|Intel",
-        short_name = "x64",
-    ),
-    triggered_by = ["Dawn Win10 x64 Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x64 Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Windows|Nvidia",
-        short_name = "x64",
-    ),
-    triggered_by = ["Dawn Win10 x64 Builder"],
-)
-
-ci.dawn_windows_builder(
-    name = "Dawn Win10 x86 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Windows|Builder",
-        short_name = "x86",
-    ),
-)
-
-ci.dawn_windows_builder(
-    name = "Dawn Win10 x86 DEPS Builder",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows|Builder",
-        short_name = "x86",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-)
-
-# Note that the Win testers are all thin Linux VMs, triggering jobs on the
-# physical Win hardware in the Swarming pool, which is why they run on linux
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x86 DEPS Release (Intel HD 630)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows|Intel",
-        short_name = "x86",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Win10 x86 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x86 DEPS Release (NVIDIA)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows|Nvidia",
-        short_name = "x86",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/Dawn Win10 x86 DEPS Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x86 Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Windows|Intel",
-        short_name = "x86",
-    ),
-    triggered_by = ["Dawn Win10 x86 Builder"],
-)
-
-ci.dawn_thin_tester(
-    name = "Dawn Win10 x86 Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT|Windows|Nvidia",
-        short_name = "x86",
-    ),
-    triggered_by = ["Dawn Win10 x86 Builder"],
-)
-
-ci.fuzz_builder(
-    name = "ASAN Debug",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux asan",
-        short_name = "dbg",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "ASan Debug (32-bit x86 with V8-ARM)",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux asan|x64 v8-ARM",
-        short_name = "dbg",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "ASAN Release",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux asan",
-        short_name = "rel",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 5,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "ASan Release (32-bit x86 with V8-ARM)",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux asan|x64 v8-ARM",
-        short_name = "rel",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "ASAN Release Media",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux asan",
-        short_name = "med",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "Afl Upload Linux ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "afl",
-        short_name = "afl",
-    ),
-    executable = "recipe:chromium_afl",
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "ASan Release Media (32-bit x86 with V8-ARM)",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux asan|x64 v8-ARM",
-        short_name = "med",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "ChromiumOS ASAN Release",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros asan",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 6,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "MSAN Release (chained origins)",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux msan",
-        short_name = "org",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "MSAN Release (no origins)",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux msan",
-        short_name = "rel",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "Mac ASAN Release",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "mac asan",
-        short_name = "rel",
-    ),
-    cores = 4,
-    os = os.MAC_DEFAULT,
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 2,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "Mac ASAN Release Media",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "mac asan",
-        short_name = "med",
-    ),
-    cores = 4,
-    os = os.MAC_DEFAULT,
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 2,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "TSAN Debug",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux tsan",
-        short_name = "dbg",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "TSAN Release",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux tsan",
-        short_name = "rel",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 3,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "UBSan Release",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux UBSan",
-        short_name = "rel",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "UBSan vptr Release",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux UBSan",
-        short_name = "vpt",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 4,
-    ),
-    goma_backend = None,
-    reclient_jobs = 250,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fuzz_builder(
-    name = "Win ASan Release",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "win asan",
-        short_name = "rel",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 7,
-    ),
-)
-
-ci.fuzz_builder(
-    name = "Win ASan Release Media",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "win asan",
-        short_name = "med",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 6,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Chrome OS ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "chromeos-asan",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 3,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload iOS Catalyst Debug",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "ios",
-    ),
-    cores = 4,
-    execution_timeout = 4 * time.hour,
-    os = os.MAC_11,
-    xcode = xcode.x13main,
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "linux",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 5,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux ASan Debug",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "linux-dbg",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 5,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux MSan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "linux-msan",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 5,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux UBSan",
-    # Do not use builderless for this (crbug.com/980080).
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "linux-ubsan",
-    ),
-    execution_timeout = 4 * time.hour,
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 5,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux V8-ARM64 ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "arm64",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 1,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux V8-ARM64 ASan Debug",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "arm64-dbg",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 1,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux32 ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "linux32",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 3,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux32 ASan Debug",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "linux32-dbg",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 3,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux32 V8-ARM ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "arm",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 1,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Linux32 V8-ARM ASan Debug",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "arm-dbg",
-    ),
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 1,
-    ),
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Mac ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "mac-asan",
-    ),
-    cores = 24,
-    execution_timeout = 4 * time.hour,
-    os = os.MAC_DEFAULT,
-)
-
-ci.fuzz_libfuzzer_builder(
-    name = "Libfuzzer Upload Windows ASan",
-    console_view_entry = consoles.console_view_entry(
-        category = "libfuzz",
-        short_name = "win-asan",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 3,
-    ),
-    # crbug.com/1175182: Temporarily increase timeout
-    execution_timeout = 4 * time.hour,
-)
-
-ci.fyi_builder(
-    name = "Linux Viz",
-    console_view_entry = consoles.console_view_entry(
-        category = "viz",
-    ),
-)
-
-ci.fyi_builder(
-    name = "Site Isolation Android",
-    console_view_entry = consoles.console_view_entry(
-        category = "site_isolation",
-    ),
-    notifies = ["Site Isolation Android"],
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fyi_builder(
-    name = "VR Linux",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    os = os.LINUX_BIONIC_REMOVE,
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.fyi_builder(
-    name = "android-backuprefptr-arm-fyi-rel",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "backuprefptr|android",
-        short_name = "32rel",
-    ),
-    notifies = ["chrome-memory-safety"],
-)
-
-ci.fyi_builder(
-    name = "android-backuprefptr-arm64-fyi-rel",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "backuprefptr|android",
-        short_name = "64rel",
-    ),
-    notifies = ["chrome-memory-safety"],
-)
-
-ci.fyi_builder(
-    name = "fuchsia-fyi-arm64-dbg",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|a64",
-            short_name = "dbg",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "fyi",
-            short_name = "a64-dbg",
-        ),
-    ],
-    notifies = ["cr-fuchsia"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "fuchsia-fyi-arm64-femu",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|a64",
-            short_name = "femu",
-        ),
-    ],
-    notifies = ["cr-fuchsia"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "fuchsia-fyi-arm64-rel",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|a64",
-            short_name = "rel",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "fyi",
-            short_name = "a64",
-        ),
-    ],
-    notifies = ["cr-fuchsia"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "fuchsia-fyi-x64-dbg",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|x64",
-            short_name = "dbg",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "fyi",
-            short_name = "x64-dbg",
-        ),
-    ],
-    notifies = ["cr-fuchsia"],
-)
-
-ci.fyi_builder(
-    name = "fuchsia-fyi-x64-rel",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|x64",
-            short_name = "rel",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "fyi",
-            short_name = "x64",
-        ),
-    ],
-    notifies = ["cr-fuchsia"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "lacros-amd64-generic-rel-fyi",
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros",
-        short_name = "lcr",
-    ),
-)
-
-ci.fyi_builder(
-    name = "linux-annotator-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "network|traffic|annotations",
-        short_name = "lnx",
-    ),
-    notifies = ["annotator-rel"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-ash-chromium-builder-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-        short_name = "lcr",
-    ),
-    properties = {
-        # The format of these properties is defined at archive/properties.proto
-        "$build/archive": {
-            "archive_datas": [
-                {
-                    "files": [
-                        "chrome",
-                        "chrome_100_percent.pak",
-                        "chrome_200_percent.pak",
-                        "chrome_crashpad_handler",
-                        "headless_lib.pak",
-                        "icudtl.dat",
-                        "libminigbm.so",
-                        "nacl_helper",
-                        "nacl_irt_x86_64.nexe",
-                        "resources.pak",
-                        "snapshot_blob.bin",
-                        "test_ash_chrome",
-                    ],
-                    "dirs": ["locales", "swiftshader"],
-                    "gcs_bucket": "ash-chromium-on-linux-prebuilts",
-                    "gcs_path": "x86_64/{%position%}/ash-chromium.zip",
-                    "archive_type": "ARCHIVE_TYPE_ZIP",
-                    "latest_upload": {
-                        "gcs_path": "x86_64/latest/ash-chromium.txt",
-                        "gcs_file_content": "{%position%}",
-                    },
-                },
-            ],
-        },
-    },
-)
-
-ci.fyi_builder(
-    name = "linux-lacros-version-skew-fyi",
-    console_view_entry = consoles.console_view_entry(
-        category = "default",
-    ),
-)
-
-ci.fyi_builder(
-    name = "linux-blink-animation-use-time-delta",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|blink",
-        short_name = "TD",
-    ),
-)
-
-ci.fyi_builder(
-    name = "linux-blink-heap-concurrent-marking-tsan-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|blink",
-        short_name = "CM",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-blink-heap-verification",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|blink",
-        short_name = "VF",
-    ),
-    notifies = ["linux-blink-fyi-bots"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-blink-v8-oilpan",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|blink",
-        short_name = "VO",
-    ),
-    notifies = ["linux-blink-fyi-bots"],
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-example-builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    schedule = "with 12h interval",
-    triggered_by = [],
-)
-
-ci.fyi_builder(
-    name = "linux-fieldtrial-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-)
-
-ci.fyi_builder(
-    name = "linux-lacros-builder-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-)
-
-ci.fyi_builder(
-    name = "linux-lacros-tester-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    triggered_by = ["linux-lacros-builder-fyi-rel"],
-)
-
-ci.fyi_builder(
-    name = "linux-lacros-dbg-fyi",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-)
-
-ci.fyi_builder(
-    name = "linux-lacros-dbg-tests-fyi",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    triggered_by = ["linux-lacros-dbg-fyi"],
-)
-
-ci.fyi_builder(
-    name = "linux-backuprefptr-x64-fyi-rel",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "backuprefptr|linux",
-        short_name = "64rel",
-    ),
-    notifies = ["chrome-memory-safety"],
-    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-)
-
-ci.fyi_builder(
-    name = "linux-perfetto-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-wpt-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    experimental = True,
-    goma_backend = goma.backend.RBE_PROD,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-wpt-identity-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    experimental = True,
-    goma_backend = goma.backend.RBE_PROD,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-wpt-input-fyi-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-    ),
-    experimental = True,
-    goma_backend = goma.backend.RBE_PROD,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-# This is launching & collecting entirely isolated tests.
-# OS shouldn't matter.
-ci.fyi_builder(
-    name = "mac-osxbeta-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "beta",
-    ),
-    goma_backend = goma.backend.RBE_PROD,
-    main_console_view = None,
-    triggered_by = ["ci/Mac Builder"],
-)
-
-ci.fyi_builder(
-    name = "linux-headless-shell-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "hdls",
-    ),
-    notifies = ["headless-owners"],
-)
-
-ci.updater_builder(
-    name = "mac-updater-builder-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "bld",
-    ),
-    os = os.MAC_ANY,
-    cpu = cpu.X86_64,
-    builderless = True,
-    cores = None,
-)
-
-ci.updater_builder(
-    name = "mac-updater-builder-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "bld",
-    ),
-    os = os.MAC_ANY,
-    cpu = cpu.X86_64,
-    builderless = True,
-    cores = None,
-)
-
-ci.updater_builder(
-    name = "mac10.11-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "10.11",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "mac10.11-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "10.11",
-    ),
-    triggered_by = ["mac-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "mac10.12-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "10.12",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "mac10.12-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "10.12",
-    ),
-    triggered_by = ["mac-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "mac10.13-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "10.13",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "mac10.13-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "10.13",
-    ),
-    triggered_by = ["mac-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "mac10.14-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "10.14",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "mac10.14-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "10.14",
-    ),
-    triggered_by = ["mac-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "mac10.15-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "10.15",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "mac10.15-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "10.15",
-    ),
-    triggered_by = ["mac-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "mac11.0-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "11.0",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "mac11.0-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "11.0",
-    ),
-    triggered_by = ["mac-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "mac-arm64-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|mac",
-        short_name = "11.0 arm64",
-    ),
-    triggered_by = ["mac-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "mac-arm64-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|mac",
-        short_name = "11.0 arm64",
-    ),
-    triggered_by = ["mac-updater-builder-rel"],
-)
-
-ci.fyi_builder(
-    name = "mac-paeverywhere-x64-fyi-dbg",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "paeverywhere|mac",
-        short_name = "64dbg",
-    ),
-    cores = None,
-    notifies = ["chrome-memory-safety"],
-    os = os.MAC_ANY,
-)
-
-ci.fyi_builder(
-    name = "mac-paeverywhere-x64-fyi-rel",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "paeverywhere|mac",
-        short_name = "64rel",
-    ),
-    cores = None,
-    notifies = ["chrome-memory-safety"],
-    os = os.MAC_ANY,
-)
-
-ci.updater_builder(
-    name = "win-updater-builder-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|win (64)",
-        short_name = "bld",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    builderless = True,
-)
-
-ci.updater_builder(
-    name = "win32-updater-builder-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|win (32)",
-        short_name = "bld",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    builderless = True,
-)
-
-ci.updater_builder(
-    name = "win-updater-builder-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|win (64)",
-        short_name = "bld",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    builderless = True,
-)
-
-ci.updater_builder(
-    name = "win32-updater-builder-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|win (32)",
-        short_name = "bld",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    builderless = True,
-)
-
-ci.updater_builder(
-    name = "win7-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|win (64)",
-        short_name = "7",
-    ),
-    triggered_by = ["win-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "win7(32)-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|win (32)",
-        short_name = "7",
-    ),
-    triggered_by = ["win32-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "win7-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|win (64)",
-        short_name = "7",
-    ),
-    triggered_by = ["win-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "win7(32)-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|win (32)",
-        short_name = "7",
-    ),
-    triggered_by = ["win32-updater-builder-rel"],
-)
-
-ci.updater_builder(
-    name = "win10-updater-tester-dbg",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|win (64)",
-        short_name = "10",
-    ),
-    triggered_by = ["win-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "win10-updater-tester-dbg-uac",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|win (64)",
-        short_name = "UAC",
-    ),
-    tree_closing = False,
-    triggered_by = ["win-updater-builder-dbg"],
-)
-
-ci.updater_builder(
-    name = "win10-updater-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|win (64)",
-        short_name = "10",
-    ),
-    triggered_by = ["win-updater-builder-rel"],
-)
-
-ci.fyi_builder(
-    name = "win-backuprefptr-x86-fyi-rel",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "backuprefptr|win",
-        short_name = "32rel",
-    ),
-    notifies = ["chrome-memory-safety"],
-    os = os.WINDOWS_ANY,
-)
-
-ci.fyi_builder(
-    name = "win-backuprefptr-x64-fyi-rel",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "backuprefptr|win",
-        short_name = "64rel",
-    ),
-    notifies = ["chrome-memory-safety"],
-    os = os.WINDOWS_ANY,
-)
-
-ci.fyi_builder(
-    name = "win-pixel-builder-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "win10",
-    ),
-    os = os.WINDOWS_10,
-)
-
-ci.fyi_builder(
-    name = "win-pixel-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "win10",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-    triggered_by = ["win-pixel-builder-rel"],
-)
-
-ci.fyi_builder(
-    name = "linux-upload-perfetto",
-    console_view_entry = consoles.console_view_entry(
-        category = "perfetto",
-        short_name = "lnx",
-    ),
-    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-)
-
-ci.fyi_builder(
-    name = "mac-upload-perfetto",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "perfetto",
-        short_name = "mac",
-    ),
-    cores = None,
-    os = os.MAC_DEFAULT,
-    schedule = "with 3h interval",
-    triggered_by = [],
-)
-
-ci.fyi_builder(
-    name = "win-upload-perfetto",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "perfetto",
-        short_name = "win",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    schedule = "with 3h interval",
-    triggered_by = [],
-)
-
-ci.fyi_builder(
-    name = "Comparison Linux",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "cmp",
-    ),
-    goma_jobs = 250,
-    executable = "recipe:reclient_goma_comparison",
-    execution_timeout = 6 * time.hour,
-    reclient_cache_silo = "Comparison Linux - cache siloed",
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = 250,
-    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-)
-
-ci.fyi_builder(
-    name = "Linux Builder (j-500) (reclient)",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux",
-        short_name = "re",
-    ),
-    goma_backend = None,
-    reclient_rewrapper_env = {
-        "RBE_platform": "container-image=docker://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:b4dad0bfc4951d619229ab15343a311f2415a16ef83bcaa55b44f4e2bf1cf635,pool=linux-e2-custom_0",
-    },
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_jobs = 500,
-    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-    schedule = "triggered",
-)
-
-# Start - Reclient migration, phase 2, block 1 shadow builders
-ci.fyi_builder(
-    name = "Linux CFI (reclient shadow)",
-    console_view_entry = consoles.console_view_entry(
-        category = "cfi",
-        short_name = "lnx",
-    ),
-    cores = 32,
-    # TODO(thakis): Remove once https://crbug.com/927738 is resolved.
-    execution_timeout = 5 * time.hour,
-    goma_backend = None,
-    reclient_jobs = 400,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-# End - Reclient migration, phase 2, block 1 shadow builders
-
-ci.fyi_windows_builder(
-    name = "Win x64 Builder (reclient)",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "win",
-        short_name = "re",
-    ),
-    cores = 32,
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    os = os.WINDOWS_DEFAULT,
-)
-
-ci.fyi_windows_builder(
-    name = "Win x64 Builder (reclient compare)",
-    builderless = True,
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-            apply_configs = ["use_clang_coverage", "enable_reclient", "reclient_test"],
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "win",
-        short_name = "re",
-    ),
-    cores = 32,
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_rewrapper_env = {"RBE_compare": "true"},
-    reclient_ensure_verified = True,
-    description_html = "verify artifacts. should be removed after the migration. crbug.com/1260232",
-    os = os.WINDOWS_DEFAULT,
-)
-
-ci.fyi_windows_builder(
-    name = "Win x64 Builder (reclient)(cross)",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "win",
-        short_name = "re x",
-    ),
-    cores = 32,
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_profiler_service = "reclient-win",
-    reclient_publish_trace = True,
-    os = os.WINDOWS_DEFAULT,
-)
-
-ci.fyi_mac_builder(
-    name = "Mac Builder (reclient)",
-    builderless = True,
-    cores = None,  # crbug.com/1245114
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "re",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    description_html = "experiment reclient on mac. should be removed after the migration. crbug.com/1244441",
-)
-
-ci.fyi_mac_builder(
-    name = "Mac Builder (reclient compare)",
-    builderless = True,
-    cores = None,  # crbug.com/1245114
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "cmp",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    reclient_rewrapper_env = {"RBE_compare": "true"},
-    reclient_ensure_verified = True,
-    description_html = "verify artifacts. should be removed after the migration. crbug.com/1260232",
-)
-
-ci.fyi_mac_builder(
-    name = "mac-arm64-on-arm64-rel-reclient",
-
-    # same with mac-arm64-on-arm64-rel
-    cores = None,  # crbug.com/1245114
-    cpu = cpu.ARM64,
-    os = os.MAC_11,
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "re",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    description_html = "experiment reclient on mac-arm. should be removed after the migration. crbug.com/1252626",
-)
-
-ci.fyi_builder(
-    name = "chromeos-amd64-generic-rel (goma cache silo)",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros x64",
-        short_name = "cgc",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "chromeos-amd64-generic-rel (reclient)",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros x64",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    os = os.LINUX_BIONIC_REMOVE,
-    reclient_rewrapper_env = {"RBE_cache_silo": "chromeos-amd64-generic-rel (reclient)"},
-)
-
-# TODO(crbug.com/1235218): remove after the migration.
-ci.fyi_builder(
-    name = "chromeos-amd64-generic-rel (reclient compare)",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros x64",
-        short_name = "cmp",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    os = os.LINUX_BIONIC_REMOVE,
-    reclient_rewrapper_env = {"RBE_compare": "true"},
-    reclient_ensure_verified = True,
-    description_html = "verify artifacts. should be removed after the migration. crbug.com/1235218",
-)
-
-ci.fyi_builder(
-    name = "lacros-amd64-generic-rel (goma cache silo)",
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros x64",
-        short_name = "cgc",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "lacros-amd64-generic-rel (reclient)",
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros x64",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    os = os.LINUX_BIONIC_REMOVE,
-    reclient_rewrapper_env = {"RBE_cache_silo": "lacros-amd64-generic-rel (reclient)"},
-)
-
-ci.fyi_builder(
-    name = "linux-lacros-builder-rel (goma cache silo)",
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros rel",
-        short_name = "cgc",
-    ),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.fyi_builder(
-    name = "linux-lacros-builder-rel (reclient)",
-    console_view_entry = consoles.console_view_entry(
-        category = "lacros rel",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    os = os.LINUX_BIONIC_REMOVE,
-    reclient_rewrapper_env = {"RBE_cache_silo": "linux-lacros-builder-rel (reclient)"},
-)
-
-ci.fyi_celab_builder(
-    name = "win-celab-builder-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "celab",
-    ),
-    schedule = "0 0,6,12,18 * * *",
-    triggered_by = [],
-)
-
-ci.fyi_celab_builder(
-    name = "win-celab-tester-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "celab",
-    ),
-    triggered_by = ["win-celab-builder-rel"],
-)
-
-ci.fyi_coverage_builder(
-    name = "android-code-coverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "and",
-    ),
-    use_java_coverage = True,
-    coverage_test_types = ["overall", "unit"],
-    schedule = "triggered",
-    triggered_by = [],
-)
-
-ci.fyi_coverage_builder(
-    name = "android-code-coverage-native",
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "ann",
-    ),
-    use_clang_coverage = True,
-    coverage_test_types = ["overall", "unit"],
-)
-
-ci.fyi_coverage_builder(
-    name = "fuchsia-code-coverage",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "code_coverage",
-            short_name = "fsa",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "misc",
-            short_name = "cov",
-        ),
-    ],
-    use_clang_coverage = True,
-    schedule = "triggered",
-    triggered_by = [],
-)
-
-ci.fyi_coverage_builder(
-    name = "ios-simulator-code-coverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "ios",
-    ),
-    cores = None,
-    os = os.MAC_11,
-    use_clang_coverage = True,
-    coverage_exclude_sources = "ios_test_files_and_test_utils",
-    coverage_test_types = ["overall", "unit"],
-    xcode = xcode.x13main,
-)
-
-ci.fyi_coverage_builder(
-    name = "linux-chromeos-code-coverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "lcr",
-    ),
-    use_clang_coverage = True,
-    coverage_test_types = ["overall", "unit"],
-    schedule = "triggered",
-    triggered_by = [],
-)
-
-ci.fyi_coverage_builder(
-    name = "linux-chromeos-js-code-coverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "jcr",
-    ),
-    use_javascript_coverage = True,
-    schedule = "triggered",
-    triggered_by = [],
-)
-
-ci.fyi_coverage_builder(
-    name = "linux-code-coverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "lnx",
-    ),
-    use_clang_coverage = True,
-    coverage_test_types = ["overall", "unit"],
-    triggered_by = [],
-)
-
-ci.fyi_coverage_builder(
-    name = "linux-lacros-code-coverage",
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "lac",
-    ),
-    use_clang_coverage = True,
-    coverage_test_types = ["overall", "unit"],
-)
-
-ci.fyi_coverage_builder(
-    name = "mac-code-coverage",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "mac",
-    ),
-    cores = 24,
-    os = os.MAC_ANY,
-    coverage_test_types = ["overall", "unit"],
-    use_clang_coverage = True,
-)
-
-ci.fyi_coverage_builder(
-    name = "win10-code-coverage",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "code_coverage",
-        short_name = "win",
-    ),
-    os = os.WINDOWS_DEFAULT,
-    coverage_test_types = ["overall", "unit"],
-    use_clang_coverage = True,
-)
-
-ci.fyi_ios_builder(
-    name = "ios-asan",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS",
-        short_name = "asan",
-    ),
-)
-ci.fyi_ios_builder(
-    name = "ios-catalyst",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "iOS",
-            short_name = "ctl",
-        ),
-    ],
-    os = os.MAC_11,
-)
-ci.fyi_ios_builder(
-    name = "ios-reclient",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS",
-        short_name = "re",
-    ),
-    goma_backend = None,
-    reclient_instance = rbe_instance.DEFAULT,
-    description_html = "experiment reclient for ios. remove after the migration. crbug.com/1254986",
-)
-
-ci.fyi_ios_builder(
-    name = "ios-simulator-cronet",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "cronet",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    notifies = ["cronet"],
-)
-
-ci.fyi_ios_builder(
-    name = "ios-simulator-multi-window",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS",
-        short_name = "mwd",
-    ),
-)
-
-ci.fyi_ios_builder(
-    name = "ios-webkit-tot",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS",
-        short_name = "wk",
-    ),
-    schedule = "0 1-23/6 * * *",
-    triggered_by = [],
-    xcode = xcode.x13wk,
-)
-
-ci.fyi_ios_builder(
-    name = "ios14-beta-simulator",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|iOS14",
-        short_name = "ios14",
-    ),
-    os = os.MAC_11,
-    schedule = "0 0,4,8,12,16,20 * * *",
-    triggered_by = [],
-)
-
-ci.fyi_ios_builder(
-    name = "ios14-sdk-simulator",
-    console_view_entry = consoles.console_view_entry(
-        category = "iOS|iOS14",
-        short_name = "sdk14",
-    ),
-    os = os.MAC_11,
-    cpu = cpu.ARM64,
-    schedule = "0 2,6,10,14,18,22 * * *",
-    triggered_by = [],
-)
-
-ci.fyi_ios_builder(
-    name = "ios15-beta-simulator",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "iOS|iOS15",
-            short_name = "ios15",
-        ),
-    ],
-    os = os.MAC_11,
-)
-
-ci.fyi_ios_builder(
-    name = "ios15-sdk-device",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "iOS|iOS15",
-            short_name = "dev",
-        ),
-    ],
-    os = os.MAC_11,
-    xcode = xcode.x13latestbeta,
-)
-
-ci.fyi_ios_builder(
-    name = "ios15-sdk-simulator",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "iOS|iOS15",
-            short_name = "sdk15",
-        ),
-    ],
-    os = os.MAC_11,
-    xcode = xcode.x13latestbeta,
-)
-
-ci.fyi_mac_builder(
-    name = "Mac Builder Next",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "bld",
-    ),
-    cores = None,
-    os = None,
-)
-
-ci.fyi_mac_builder(
-    name = "Mac deterministic",
-    console_view_entry = consoles.console_view_entry(
-        category = "deterministic|mac",
-        short_name = "rel",
-    ),
-    cores = None,
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-)
-
-ci.fyi_mac_builder(
-    name = "Mac deterministic (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "deterministic|mac",
-        short_name = "dbg",
-    ),
-    cores = None,
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-    os = os.MAC_DEFAULT,
-)
-
-ci.fyi_mac_builder(
-    name = "mac-hermetic-upgrade-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "herm",
-    ),
-    cores = 12,
-)
-
-ci.fyi_windows_builder(
-    name = "Win 10 Fast Ring",
-    console_view_entry = consoles.console_view_entry(
-        category = "win10",
-    ),
-    os = os.WINDOWS_10,
-    notifies = ["Win 10 Fast Ring"],
-)
-
-ci.fyi_windows_builder(
-    name = "Win10 Tests x64 20h2",
-    console_view_entry = consoles.console_view_entry(
-        category = "win10|20h2",
-    ),
-    goma_backend = None,
-    main_console_view = None,
-    os = os.WINDOWS_10,
-    triggered_by = ["ci/Win x64 Builder"],
-)
-
-ci.fyi_windows_builder(
-    name = "win32-arm64-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "win32|arm64",
-    ),
-    cpu = cpu.X86,
-    goma_jobs = goma.jobs.J150,
-)
-
-ci.fyi_windows_builder(
-    name = "win-annotator-rel",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "network|traffic|annotations",
-        short_name = "win",
-    ),
-    execution_timeout = 16 * time.hour,
-    notifies = ["annotator-rel"],
-)
-
-ci.gpu_linux_builder(
-    name = "Android Release (Nexus 5X)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "Android",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_linux_builder(
-    name = "GPU Linux Builder",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-)
-
-ci.gpu_linux_builder(
-    name = "GPU Linux Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux",
-    ),
-    tree_closing = False,
-)
-
-ci.gpu_mac_builder(
-    name = "GPU Mac Builder",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-)
-
-ci.gpu_mac_builder(
-    name = "GPU Mac Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac",
-    ),
-    tree_closing = False,
-)
-
-ci.gpu_windows_builder(
-    name = "GPU Win x64 Builder",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-)
-
-ci.gpu_windows_builder(
-    name = "GPU Win x64 Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows",
-    ),
-    tree_closing = False,
-)
-
-ci.gpu_thin_tester(
-    name = "Linux Debug (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux",
-    ),
-    triggered_by = ["GPU Linux Builder (dbg)"],
-    tree_closing = False,
-)
-
-ci.gpu_thin_tester(
-    name = "Linux Release (NVIDIA)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    cq_mirrors_console_view = "mirrors",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux",
-    ),
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/GPU Linux Builder"],
-)
-
-ci.gpu_thin_tester(
-    name = "Mac Debug (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac",
-    ),
-    triggered_by = ["GPU Mac Builder (dbg)"],
-    tree_closing = False,
-)
-
-ci.gpu_thin_tester(
-    name = "Mac Release (Intel)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/GPU Mac Builder"],
-)
-
-ci.gpu_thin_tester(
-    name = "Mac Retina Debug (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac",
-    ),
-    triggered_by = ["GPU Mac Builder (dbg)"],
-    tree_closing = False,
-)
-
-ci.gpu_thin_tester(
-    name = "Mac Retina Release (AMD)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/GPU Mac Builder"],
-)
-
-ci.gpu_thin_tester(
-    name = "Win10 x64 Debug (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows",
-    ),
-    triggered_by = ["GPU Win x64 Builder (dbg)"],
-    tree_closing = False,
-)
-
-ci.gpu_thin_tester(
-    name = "Win10 x64 Release (NVIDIA)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = main_console_if_on_branch(),
-    triggered_by = ["ci/GPU Win x64 Builder"],
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI Release (NVIDIA Shield TV)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|N64|NVDA",
-        short_name = "STV",
-    ),
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI Release (Nexus 5)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|L32",
-        short_name = "N5",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI Release (Nexus 5X)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|M64|QCOM",
-        short_name = "N5X",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI Release (Nexus 9)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|M64|NVDA",
-        short_name = "N9",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI Release (Pixel 2)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|P32|QCOM",
-        short_name = "P2",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI Release (Pixel 4)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|R32|QCOM",
-        short_name = "P4",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI SkiaRenderer GL (Nexus 5X)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|skgl|M64",
-        short_name = "N5X",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Android FYI SkiaRenderer Vulkan (Pixel 2)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Android|skv|P32",
-        short_name = "P2",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "ChromeOS FYI Release (amd64-generic)",
-    # Runs a lot of tests + VMs are slower than real hardware, so increase the
-    # timeout.
-    execution_timeout = 8 * time.hour,
-    console_view_entry = consoles.console_view_entry(
-        category = "ChromeOS|amd64|generic",
-        short_name = "x64",
-    ),
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "ChromeOS FYI Release (kevin)",
-    console_view_entry = consoles.console_view_entry(
-        category = "ChromeOS|arm|kevin",
-        short_name = "kvn",
-    ),
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "GPU FYI Lacros x64 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Lacros|Builder",
-        short_name = "rel",
-    ),
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "GPU FYI Linux Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Builder",
-        short_name = "rel",
-    ),
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "GPU FYI Linux Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Builder",
-        short_name = "dbg",
-    ),
-)
-
-ci.gpu_fyi_linux_builder(
-    name = "Linux FYI GPU TSAN Release",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux",
-        short_name = "tsn",
-    ),
-)
-
-# Builder + tester.
-ci.gpu_fyi_linux_builder(
-    name = "Linux FYI SkiaRenderer Dawn Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Intel",
-        short_name = "skd",
-    ),
-)
-
-ci.gpu_fyi_mac_builder(
-    name = "GPU FYI Mac Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Builder",
-        short_name = "rel",
-    ),
-)
-
-ci.gpu_fyi_mac_builder(
-    name = "GPU FYI Mac Builder (asan)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Builder",
-        short_name = "asn",
-    ),
-)
-
-ci.gpu_fyi_mac_builder(
-    name = "GPU FYI Mac Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Builder",
-        short_name = "dbg",
-    ),
-)
-
-ci.gpu_fyi_mac_builder(
-    name = "GPU FYI Mac arm64 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Builder",
-        short_name = "arm",
-    ),
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Lacros FYI x64 Release (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Lacros|AMD",
-        short_name = "amd",
-    ),
-    triggered_by = ["GPU FYI Lacros x64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Lacros FYI x64 Release (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Lacros|Intel",
-        short_name = "int",
-    ),
-    triggered_by = ["GPU FYI Lacros x64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI Debug (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Nvidia",
-        short_name = "dbg",
-    ),
-    triggered_by = ["GPU FYI Linux Builder (dbg)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI Experimental Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Intel",
-        short_name = "exp",
-    ),
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI Experimental Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Nvidia",
-        short_name = "exp",
-    ),
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Nvidia",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI Release (AMD RX 5500 XT)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|AMD",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Intel",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI Release (Intel UHD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Intel",
-        short_name = "uhd",
-    ),
-    # TODO(https://crbug.com/986939): Remove this increased timeout once more
-    # devices are added.
-    execution_timeout = 18 * time.hour,
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI SkiaRenderer Vulkan (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Intel",
-        short_name = "skv",
-    ),
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Linux FYI SkiaRenderer Vulkan (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Linux|Nvidia",
-        short_name = "skv",
-    ),
-    triggered_by = ["GPU FYI Linux Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Debug (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Intel",
-        short_name = "dbg",
-    ),
-    triggered_by = ["GPU FYI Mac Builder (dbg)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Experimental Release (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Intel",
-        short_name = "exp",
-    ),
-    triggered_by = ["GPU FYI Mac Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Experimental Retina Release (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|AMD|Retina",
-        short_name = "exp",
-    ),
-    triggered_by = ["GPU FYI Mac Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Experimental Retina Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Nvidia",
-        short_name = "exp",
-    ),
-    # This bot has one machine backing its tests at the moment.
-    # If it gets more, this can be removed.
-    # See crbug.com/853307 for more context.
-    execution_timeout = 12 * time.hour,
-    triggered_by = ["GPU FYI Mac Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Release (Apple M1)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Apple",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Mac arm64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI ASAN (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Intel",
-        short_name = "asn",
-    ),
-    triggered_by = ["GPU FYI Mac Builder (asan)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Release (Intel)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Intel",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Mac Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Retina ASAN (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|AMD|Retina",
-        short_name = "asn",
-    ),
-    triggered_by = ["GPU FYI Mac Builder (asan)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Retina Debug (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|AMD|Retina",
-        short_name = "dbg",
-    ),
-    triggered_by = ["GPU FYI Mac Builder (dbg)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Retina Debug (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Nvidia",
-        short_name = "dbg",
-    ),
-    triggered_by = ["GPU FYI Mac Builder (dbg)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Retina Release (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|AMD|Retina",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Mac Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac FYI Retina Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|Nvidia",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Mac Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Mac Pro FYI Release (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Mac|AMD|Pro",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Mac Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 Debug (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Nvidia",
-        short_name = "dbg",
-    ),
-    triggered_by = ["GPU FYI Win x64 Builder (dbg)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Nvidia|dx12vk",
-        short_name = "dbg",
-    ),
-    triggered_by = ["GPU FYI Win x64 DX12 Vulkan Builder (dbg)"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Nvidia|dx12vk",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win x64 DX12 Vulkan Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 Exp Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Intel",
-        short_name = "exp",
-    ),
-    triggered_by = ["GPU FYI Win x64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 Exp Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Nvidia",
-        short_name = "exp",
-    ),
-    triggered_by = ["GPU FYI Win x64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 Release (AMD RX 5500 XT)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|AMD",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win x64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 Release (Intel HD 630)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Intel",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win x64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Nvidia",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win x64 Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x64 Release XR Perf (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Nvidia",
-        short_name = "xr",
-    ),
-    triggered_by = ["GPU FYI XR Win x64 Builder"],
-)
-
-# Builder + tester.
-ci.gpu_fyi_windows_builder(
-    name = "Win10 FYI x64 SkiaRenderer Dawn Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x64|Nvidia",
-        short_name = "skd",
-    ),
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win10 FYI x86 Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|10|x86|Nvidia",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win7 FYI Release (AMD)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|7|x86|AMD",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win7 FYI Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|7|x86|Nvidia",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win Builder"],
-)
-
-ci.gpu_fyi_thin_tester(
-    name = "Win7 FYI x64 Release (NVIDIA)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|7|x64|Nvidia",
-        short_name = "rel",
-    ),
-    triggered_by = ["GPU FYI Win x64 Builder"],
-)
-
-ci.gpu_fyi_windows_builder(
-    name = "GPU FYI Win Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|Release",
-        short_name = "x86",
-    ),
-)
-
-ci.gpu_fyi_windows_builder(
-    name = "GPU FYI Win x64 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|Release",
-        short_name = "x64",
-    ),
-)
-
-ci.gpu_fyi_windows_builder(
-    name = "GPU FYI Win x64 Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|Debug",
-        short_name = "x64",
-    ),
-)
-
-ci.gpu_fyi_windows_builder(
-    name = "GPU FYI Win x64 DX12 Vulkan Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|dx12vk",
-        short_name = "rel",
-    ),
-)
-
-ci.gpu_fyi_windows_builder(
-    name = "GPU FYI Win x64 DX12 Vulkan Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|dx12vk",
-        short_name = "dbg",
-    ),
-)
-
-ci.gpu_fyi_windows_builder(
-    name = "GPU FYI XR Win x64 Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "Windows|Builder|XR",
-        short_name = "x64",
-    ),
-)
-
-ci.linux_builder(
-    name = "Cast Audio Linux",
-    console_view_entry = consoles.console_view_entry(
-        category = "cast",
-        short_name = "aud",
-    ),
-    main_console_view = "main",
-    ssd = True,
-)
-
-ci.linux_builder(
-    name = "Cast Linux",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "cast",
-        short_name = "vid",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    goma_jobs = goma.jobs.J50,
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "Cast Linux Debug",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "cast",
-        short_name = "dbg",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.LINUX_BIONIC,
-    # TODO(crbug.com/1173333): Make it tree-closing.
-    tree_closing = False,
-)
-
-ci.linux_builder(
-    name = "Cast Linux ARM64",
-    branch_selector = branches.MAIN,
-    console_view_entry = consoles.console_view_entry(
-        category = "cast",
-        short_name = "arm64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.LINUX_BIONIC,
-    tree_closing = False,
-)
-
-ci.linux_builder(
-    name = "Deterministic Fuchsia (dbg)",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|x64",
-            short_name = "det",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "misc",
-            short_name = "det",
-        ),
-    ],
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-    goma_jobs = None,
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "Deterministic Linux",
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "det",
-    ),
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-    main_console_view = "main",
-    # Set tree_closing to false to disable the defaualt tree closer, which
-    # filters by step name, and instead enable tree closing for any step
-    # failure.
-    tree_closing = False,
-    extra_notifies = ["Deterministic Linux", "close-on-any-step-failure"],
-)
-
-ci.linux_builder(
-    name = "Deterministic Linux (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|builder",
-        short_name = "det",
-    ),
-    cores = 32,
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 7 * time.hour,
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "Fuchsia ARM64",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|a64",
-            short_name = "rel",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "ci",
-            short_name = "arm64",
-        ),
-    ],
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    extra_notifies = ["cr-fuchsia"],
-)
-
-ci.linux_builder(
-    name = "Fuchsia x64",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|x64",
-            short_name = "rel",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "ci",
-            short_name = "x64",
-        ),
-    ],
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    extra_notifies = ["cr-fuchsia"],
-)
-
-ci.linux_builder(
-    name = "Leak Detection Linux",
-    console_view_entry = consoles.console_view_entry(
-        console_view = "chromium.fyi",
-        category = "linux",
-        short_name = "lk",
-    ),
-    notifies = [],
-    tree_closing = False,
-)
-
-ci.linux_builder(
-    name = "Linux Builder",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "bld",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "Linux Builder (dbg)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|builder",
-        short_name = "64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.linux_builder(
-    name = "Linux Builder (dbg)(32)",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|builder",
-        short_name = "32",
-    ),
-    main_console_view = "main",
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.linux_builder(
-    name = "Linux Builder (Wayland)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "bld-wl",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.linux_builder(
-    name = "Linux Tests",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "tst",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    goma_backend = None,
-    main_console_view = "main",
-    triggered_by = ["ci/Linux Builder"],
-)
-
-ci.linux_builder(
-    name = "Linux Tests (dbg)(1)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|tester",
-        short_name = "64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Linux Builder (dbg)"],
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.linux_builder(
-    name = "Linux Tests (Wayland)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "tst-wl",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    goma_backend = None,
-    main_console_view = "main",
-    triggered_by = ["ci/Linux Builder (Wayland)"],
-)
-
-ci.linux_builder(
-    name = "fuchsia-arm64-cast",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|cast",
-            short_name = "a64",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "ci",
-            short_name = "arm64-cast",
-        ),
-    ],
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    # Set tree_closing to false to disable the defaualt tree closer, which
-    # filters by step name, and instead enable tree closing for any step
-    # failure.
-    tree_closing = False,
-    extra_notifies = ["cr-fuchsia", "close-on-any-step-failure"],
-)
-
-ci.linux_builder(
-    name = "Network Service Linux",
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "nsl",
-    ),
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "fuchsia-x64-cast",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|cast",
-            short_name = "x64",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "ci",
-            short_name = "x64-cast",
-        ),
-    ],
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    # Set tree_closing to false to disable the defaualt tree closer, which
-    # filters by step name, and instead enable tree closing for any step
-    # failure.
-    tree_closing = False,
-    extra_notifies = ["cr-fuchsia", "close-on-any-step-failure"],
-)
-
-ci.linux_builder(
-    name = "fuchsia-x64-dbg",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "fuchsia|x64",
-            short_name = "dbg",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.fuchsia",
-            category = "ci",
-            short_name = "x64-dbg",
-        ),
-    ],
-    main_console_view = "main",
-    extra_notifies = ["cr-fuchsia"],
-)
-
-ci.linux_builder(
-    name = "linux-bfcache-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "bfcache",
-        short_name = "bfc",
-    ),
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "linux-extended-tracing-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "trc",
-    ),
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "linux-gcc-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "gcc",
-    ),
-    goma_backend = None,
-    main_console_view = "main",
-)
-
-ci.linux_builder(
-    name = "linux-bionic-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "bio",
-    ),
-    main_console_view = "main",
-    os = os.LINUX_BIONIC,
-    tree_closing = False,
-)
-
-ci.linux_builder(
-    name = "linux-trusty-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "tru",
-    ),
-    main_console_view = "main",
-    os = os.LINUX_TRUSTY,
-)
-
-ci.linux_builder(
-    name = "linux-xenial-rel",
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "xen",
-    ),
-    main_console_view = "main",
-    os = os.LINUX_XENIAL,
-)
-
-ci.linux_builder(
-    name = "metadata-exporter",
-    console_view_entry = consoles.console_view_entry(
-        console_view = "metadata.exporter",
-    ),
-    executable = "recipe:chromium_export_metadata",
-    service_account = "component-mapping-updater@chops-service-accounts.iam.gserviceaccount.com",
-    notifies = ["metadata-mapping"],
-    tree_closing = False,
-)
-
-ci.infra_builder(
-    name = "linux-bootstrap",
-    bootstrap = True,
-    builder_spec = builder_config.builder_spec(
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "bootstrap|linux",
-        short_name = "bld",
-    ),
-    triggered_by = [],
-    schedule = "triggered",
-)
-
-ci.infra_builder(
-    name = "linux-bootstrap-tests",
-    bootstrap = True,
-    builder_spec = builder_config.builder_spec(
-        execution_mode = builder_config.execution_mode.TEST,
-        parent = "ci/linux-bootstrap",
-        chromium_config = builder_config.chromium_config(
-            config = "chromium",
-            apply_configs = ["mb"],
-            build_config = builder_config.build_config.RELEASE,
-            target_bits = 64,
-        ),
-        gclient_config = builder_config.gclient_config(
-            config = "chromium",
-        ),
-    ),
-    console_view_entry = consoles.console_view_entry(
-        category = "bootstrap|linux",
-        short_name = "tst",
-    ),
-)
-
-ci.infra_builder(
-    name = "win-bootstrap",
-    bootstrap = True,
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "bootstrap|win",
-        short_name = "bld",
-    ),
-    os = os.WINDOWS_10,
-    triggered_by = [],
-    schedule = "triggered",
-)
-
-ci.infra_builder(
-    name = "win-bootstrap-tests",
-    bootstrap = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "bootstrap|win",
-        short_name = "tst",
-    ),
-    triggered_by = ["ci/win-bootstrap"],
-)
-
-ci.mac_builder(
-    name = "Mac Builder",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "bld",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.MAC_DEFAULT,
-)
-
-ci.mac_builder(
-    name = "Mac Builder (dbg)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug",
-        short_name = "bld",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.MAC_ANY,
-)
-
-ci.mac_builder(
-    name = "mac-arm64-on-arm64-rel",
-
-    # TODO(crbug.com/1186823): Expand to more branches when all M1 bots are
-    # rosettaless.
-    branch_selector = branches.MAIN,
-    console_view_entry = consoles.console_view_entry(
-        category = "release|arm64",
-        short_name = "a64",
-    ),
-    main_console_view = "main",
-    cores = None,
-    cpu = cpu.ARM64,
-    os = os.MAC_11,
-)
-
-ci.mac_builder(
-    name = "mac-arm64-rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release|arm64",
-        short_name = "bld",
-    ),
-    main_console_view = "main",
-    cores = None,
-    os = os.MAC_ANY,
-)
-
-ci.mac_thin_tester(
-    name = "mac11-arm64-rel-tests",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release|arm64",
-        short_name = "11",
-    ),
-    tree_closing = False,
-    main_console_view = "main",
-    triggered_by = ["ci/mac-arm64-rel"],
-)
-
-ci.mac_thin_tester(
-    name = "Mac10.11 Tests",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "11",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder"],
-)
-
-ci.mac_thin_tester(
-    name = "Mac10.12 Tests",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "12",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder"],
-)
-
-ci.mac_thin_tester(
-    name = "Mac10.13 Tests",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "13",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder"],
-)
-
-ci.mac_thin_tester(
-    name = "Mac10.14 Tests",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "14",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder"],
-)
-
-ci.mac_thin_tester(
-    name = "Mac10.15 Tests",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release",
-        short_name = "15",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder"],
-)
-
-ci.mac_thin_tester(
-    name = "Mac11 Tests",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "11",
-    ),
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder"],
-)
-
-ci.mac_thin_tester(
-    name = "Mac11 Tests (dbg)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug",
-        short_name = "11",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Mac Builder (dbg)"],
-)
-
-ci.mac_ios_builder(
-    name = "ios-device",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "ios|default",
-            short_name = "dev",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.ios",
-            category = "chromium.mac",
-            short_name = "dev",
-        ),
-    ],
-    # We don't have necessary capacity to run this configuration in CQ, but it
-    # is part of the main waterfall
-    main_console_view = "main",
-)
-
-ci.mac_ios_builder(
-    name = "ios-simulator",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "ios|default",
-            short_name = "sim",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.ios",
-            category = "chromium.mac",
-            short_name = "sim",
-        ),
-    ],
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.mac_ios_builder(
-    name = "ios-simulator-full-configs",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "ios|default",
-            short_name = "ful",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.ios",
-            category = "chromium.mac",
-            short_name = "ful",
-        ),
-    ],
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-)
-
-ci.mac_ios_builder(
-    name = "ios-simulator-noncq",
-    console_view_entry = [
-        consoles.console_view_entry(
-            category = "ios|default",
-            short_name = "non",
-        ),
-        consoles.console_view_entry(
-            branch_selector = branches.MAIN,
-            console_view = "sheriff.ios",
-            category = "chromium.mac",
-            short_name = "non",
-        ),
-    ],
-    # We don't have necessary capacity to run this configuration in CQ, but it
-    # is part of the main waterfall
-    main_console_view = "main",
-    xcode = xcode.x13main,
-)
-
-ci.memory_builder(
-    name = "Linux ASan LSan Builder",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|asan lsan",
-        short_name = "bld",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.LINUX_BIONIC,
-    ssd = True,
-)
-
-ci.memory_builder(
-    name = "Linux ASan LSan Tests (1)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|asan lsan",
-        short_name = "tst",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Linux ASan LSan Builder"],
-    os = os.LINUX_BIONIC,
-)
-
-ci.memory_builder(
-    name = "Linux ASan Tests (sandboxed)",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|asan lsan",
-        short_name = "sbx",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Linux ASan LSan Builder"],
-)
-
-ci.memory_builder(
-    name = "Linux TSan Builder",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|TSan v2",
-        short_name = "bld",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.memory_builder(
-    name = "Linux CFI",
-    console_view_entry = consoles.console_view_entry(
-        category = "cfi",
-        short_name = "lnx",
-    ),
-    cores = 32,
-    # TODO(thakis): Remove once https://crbug.com/927738 is resolved.
-    execution_timeout = 5 * time.hour,
-    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
-    main_console_view = "main",
-)
-
-ci.memory_builder(
-    name = "Linux Chromium OS ASan LSan Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros|asan",
-        short_name = "bld",
-    ),
-    # TODO(crbug.com/1030593): Builds take more than 3 hours sometimes. Remove
-    # once the builds are faster.
-    execution_timeout = 6 * time.hour,
-    main_console_view = "main",
-)
-
-ci.memory_builder(
-    name = "Linux Chromium OS ASan LSan Tests (1)",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros|asan",
-        short_name = "tst",
-    ),
-    triggered_by = ["Linux Chromium OS ASan LSan Builder"],
-    main_console_view = "main",
-)
-
-ci.memory_builder(
-    name = "Linux ChromiumOS MSan Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros|msan",
-        short_name = "bld",
-    ),
-    main_console_view = "main",
-    execution_timeout = 4 * time.hour,
-)
-
-ci.memory_builder(
-    name = "Linux ChromiumOS MSan Tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "cros|msan",
-        short_name = "tst",
-    ),
-    execution_timeout = 4 * time.hour,
-    triggered_by = ["Linux ChromiumOS MSan Builder"],
-    main_console_view = "main",
-)
-
-ci.memory_builder(
-    name = "Linux MSan Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|msan",
-        short_name = "bld",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-    main_console_view = "main",
-)
-
-ci.memory_builder(
-    name = "Linux MSan Tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|msan",
-        short_name = "tst",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.LOW_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-    triggered_by = ["Linux MSan Builder"],
-    main_console_view = "main",
-)
-
-ci.memory_builder(
-    name = "Mac ASan 64 Builder",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "bld",
-    ),
-    goma_debug = True,  # TODO(hinoka): Remove this after debugging.
-    goma_jobs = None,
-    cores = None,  # Swapping between 8 and 24
-    main_console_view = "main",
-    os = os.MAC_DEFAULT,
-    triggering_policy = scheduler.greedy_batching(
-        max_concurrent_invocations = 2,
-    ),
-)
-
-ci.memory_builder(
-    name = "Linux TSan Tests",
-    branch_selector = branches.STANDARD_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|TSan v2",
-        short_name = "tst",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    triggered_by = ["ci/Linux TSan Builder"],
-    main_console_view = "main",
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.LOW_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.memory_builder(
-    name = "Mac ASan 64 Tests (1)",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "mac",
-        short_name = "tst",
-    ),
-    cores = 12,
-    main_console_view = "main",
-    os = os.MAC_DEFAULT,
-    triggered_by = ["Mac ASan 64 Builder"],
-)
-
-ci.memory_builder(
-    name = "WebKit Linux ASAN",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|webkit",
-        short_name = "asn",
-    ),
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.memory_builder(
-    name = "WebKit Linux Leak",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|webkit",
-        short_name = "lk",
-    ),
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.memory_builder(
-    name = "WebKit Linux MSAN",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|webkit",
-        short_name = "msn",
-    ),
-    main_console_view = "main",
-    os = os.LINUX_BIONIC_REMOVE,
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.memory_builder(
-    name = "android-asan",
-    console_view_entry = consoles.console_view_entry(
-        category = "android",
-        short_name = "asn",
-    ),
-    main_console_view = "main",
-    sheriff_rotations = sheriff_rotations.ANDROID,
-    tree_closing = False,
-)
-
-ci.memory_builder(
-    name = "linux-ubsan-vptr",
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|ubsan",
-        short_name = "vpt",
-    ),
-    builderless = 1,
-    cores = 32,
-    main_console_view = "main",
-    tree_closing = False,
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-ci.memory_builder(
-    name = "win-asan",
-    console_view_entry = consoles.console_view_entry(
-        category = "win",
-        short_name = "asn",
-    ),
-    cores = 32,
-    builderless = True,
-    main_console_view = "main",
-    os = os.WINDOWS_DEFAULT,
-)
-
-ci.mojo_builder(
-    name = "Mojo Android",
-    console_view_entry = consoles.console_view_entry(
-        short_name = "and",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.mojo_builder(
-    name = "Mojo ChromiumOS",
-    console_view_entry = consoles.console_view_entry(
-        short_name = "cr",
-    ),
-)
-
-ci.mojo_builder(
-    name = "Mojo Linux",
-    console_view_entry = consoles.console_view_entry(
-        short_name = "lnx",
-    ),
-    goma_backend = None,
-    reclient_jobs = rbe_jobs.DEFAULT,
-    reclient_instance = rbe_instance.DEFAULT,
-)
-
-ci.mojo_builder(
-    name = "Mojo Windows",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        short_name = "win",
-    ),
-    os = os.WINDOWS_DEFAULT,
-)
-
-ci.mojo_builder(
-    name = "mac-mojo-rel",
-    console_view_entry = consoles.console_view_entry(
-        short_name = "mac",
-    ),
-    cores = 4,
-    os = os.MAC_ANY,
-)
-
-ci.rust_builder(
-    name = "android-rust-arm-rel",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "android|rel",
-        short_name = "32",
-    ),
-    notifies = ["chrome-memory-safety"],
-    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-)
-
-ci.rust_builder(
-    name = "linux-rust-x64-rel",
-    builderless = False,
-    console_view_entry = consoles.console_view_entry(
-        category = "linux|rel",
-        short_name = "64",
-    ),
-    notifies = ["chrome-memory-safety"],
-    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
-)
-
-ci.swangle_linux_builder(
-    name = "linux-swangle-chromium-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "Chromium|Linux",
-        short_name = "x64",
-    ),
-    pinned = False,
-)
-
-ci.swangle_linux_builder(
-    name = "linux-swangle-tot-angle-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT ANGLE|Linux",
-        short_name = "x64",
-    ),
-)
-
-ci.swangle_linux_builder(
-    name = "linux-swangle-tot-swiftshader-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT SwiftShader|Linux",
-        short_name = "x64",
-    ),
-)
-
-ci.swangle_linux_builder(
-    name = "linux-swangle-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Linux",
-        short_name = "x64",
-    ),
-    pinned = False,
-)
-
-ci.swangle_mac_builder(
-    name = "mac-swangle-chromium-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "Chromium|Mac",
-        short_name = "x64",
-    ),
-    pinned = False,
-)
-
-ci.swangle_windows_builder(
-    name = "win-swangle-chromium-x86",
-    console_view_entry = consoles.console_view_entry(
-        category = "Chromium|Windows",
-        short_name = "x86",
-    ),
-    pinned = False,
-)
-
-ci.swangle_windows_builder(
-    name = "win-swangle-tot-angle-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT ANGLE|Windows",
-        short_name = "x64",
-    ),
-)
-
-ci.swangle_windows_builder(
-    name = "win-swangle-tot-angle-x86",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT ANGLE|Windows",
-        short_name = "x86",
-    ),
-)
-
-ci.swangle_windows_builder(
-    name = "win-swangle-tot-swiftshader-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT SwiftShader|Windows",
-        short_name = "x64",
-    ),
-)
-
-ci.swangle_windows_builder(
-    name = "win-swangle-tot-swiftshader-x86",
-    console_view_entry = consoles.console_view_entry(
-        category = "ToT SwiftShader|Windows",
-        short_name = "x86",
-    ),
-)
-
-ci.swangle_windows_builder(
-    name = "win-swangle-x64",
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows",
-        short_name = "x64",
-    ),
-    pinned = False,
-)
-
-ci.swangle_windows_builder(
-    name = "win-swangle-x86",
-    console_view_entry = consoles.console_view_entry(
-        category = "DEPS|Windows",
-        short_name = "x86",
-    ),
-    pinned = False,
-)
-
-ci.win_builder(
-    name = "WebKit Win10",
-    console_view_entry = consoles.console_view_entry(
-        category = "misc",
-        short_name = "wbk",
-    ),
-    main_console_view = "main",
-    triggered_by = ["Win Builder"],
-)
-
-ci.win_builder(
-    name = "Win Builder",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|builder",
-        short_name = "32",
-    ),
-    cores = 32,
-    main_console_view = "main",
-    os = os.WINDOWS_ANY,
-)
-
-ci.win_builder(
-    name = "Win x64 Builder (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|builder",
-        short_name = "64",
-    ),
-    cores = 32,
-    builderless = True,
-    main_console_view = "main",
-    os = os.WINDOWS_ANY,
-)
-
-ci.win_builder(
-    name = "Win10 Tests x64 (dbg)",
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|tester",
-        short_name = "10",
-    ),
-    main_console_view = "main",
-    triggered_by = ["Win x64 Builder (dbg)"],
-    # Too flaky. See crbug.com/876224 for more details.
-    tree_closing = False,
-)
-
-ci.win_thin_tester(
-    name = "Win7 (32) Tests",
-    console_view_entry = consoles.console_view_entry(
-        category = "release|tester",
-        short_name = "32",
-    ),
-    main_console_view = "main",
-    triggered_by = ["Win Builder"],
-)
-
-ci.win_builder(
-    name = "Win7 Tests (1)",
-    builderless = True,
-    console_view_entry = consoles.console_view_entry(
-        category = "release|tester",
-        short_name = "32",
-    ),
-    main_console_view = "main",
-    os = os.WINDOWS_10,
-    triggered_by = ["Win Builder"],
-)
-
-ci.win_builder(
-    name = "Win7 Tests (dbg)(1)",
-    builderless = True,
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|tester",
-        short_name = "7",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    os = os.WINDOWS_10,
-    main_console_view = "main",
-    triggered_by = ["ci/Win Builder (dbg)"],
-)
-
-ci.win_builder(
-    name = "Win 7 Tests x64 (1)",
-    builderless = True,
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release|tester",
-        short_name = "64",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    os = os.WINDOWS_10,
-    main_console_view = "main",
-    triggered_by = ["ci/Win x64 Builder"],
-)
-
-ci.win_builder(
-    name = "Win Builder (dbg)",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "debug|builder",
-        short_name = "32",
-    ),
-    cores = 32,
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.WINDOWS_ANY,
-)
-
-ci.win_builder(
-    name = "Win x64 Builder",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release|builder",
-        short_name = "64",
-    ),
-    cores = 32,
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    os = os.WINDOWS_ANY,
-)
-
-ci.win_builder(
-    name = "Win10 Tests x64",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    console_view_entry = consoles.console_view_entry(
-        category = "release|tester",
-        short_name = "w10",
-    ),
-    cq_mirrors_console_view = "mirrors",
-    main_console_view = "main",
-    triggered_by = ["ci/Win x64 Builder"],
-)
-
-ci.win_builder(
-    name = "Windows deterministic",
-    console_view_entry = consoles.console_view_entry(
-        category = "misc",
-        short_name = "det",
-    ),
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 12 * time.hour,
-    goma_jobs = goma.jobs.J150,
-    main_console_view = "main",
-)
-
-ci.cipd_builder(
-    name = "rts-model-packager",
-    builderless = False,
-    executable = "recipe:chromium_rts/create_model",
-    schedule = "0 9 * * *",  # at 1AM or 2AM PT (depending on DST), once a day.
-    triggered_by = [],
-    execution_timeout = 8 * time.hour,
-    cores = None,
-    console_view_entry = consoles.console_view_entry(
-        category = "rts",
-        short_name = "create-model",
-    ),
-    notifies = [
-        luci.notifier(
-            name = "rts-model-packager-notifier",
-            on_occurrence = ["FAILURE", "INFRA_FAILURE"],
-            notify_emails = ["chrome-browser-infra-team@google.com"],
-        ),
-    ],
-)
+exec("./ci/chromium.star")
+exec("./ci/chromium.android.star")
+exec("./ci/chromium.android.fyi.star")
+exec("./ci/chromium.angle.star")
+exec("./ci/chromium.chromiumos.star")
+exec("./ci/chromium.clang.star")
+exec("./ci/chromium.dawn.star")
+exec("./ci/chromium.fuzz.star")
+exec("./ci/chromium.fyi.star")
+exec("./ci/chromium.gpu.star")
+exec("./ci/chromium.gpu.fyi.star")
+exec("./ci/chromium.linux.star")
+exec("./ci/chromium.mac.star")
+exec("./ci/chromium.memory.star")
+exec("./ci/chromium.mojo.star")
+exec("./ci/chromium.packager.star")
+exec("./ci/chromium.rust.star")
+exec("./ci/chromium.swangle.star")
+exec("./ci/chromium.updater.star")
+exec("./ci/chromium.win.star")
+exec("./ci/infra.star")
+exec("./ci/metadata.exporter.star")
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.fyi.star b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
new file mode 100644
index 0000000..d2eaf884
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.android.fyi.star
@@ -0,0 +1,177 @@
+# 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.
+"""Definitions of builders in the chromium.android.fyi builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.android.fyi",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.console_view(
+    name = "chromium.android.fyi",
+    ordering = {
+        None: ["android", "memory", "weblayer", "webview"],
+    },
+)
+
+ci.builder(
+    name = "Android ASAN (dbg) (reclient)",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|arm",
+        short_name = "san",
+    ),
+    # Higher build timeout since dbg ASAN builds can take a while on a clobber
+    # build.
+    execution_timeout = 4 * time.hour,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = 150,
+    schedule = "triggered",  # triggered manually via Scheduler UI
+)
+
+ci.builder(
+    name = "android-pie-arm64-wpt-rel-non-cq",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|arm64",
+        short_name = "P-WPT",
+    ),
+)
+
+ci.builder(
+    name = "android-web-platform-pie-x86-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|web-platform",
+        short_name = "P",
+    ),
+)
+
+ci.builder(
+    name = "android-weblayer-pie-x86-wpt-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|weblayer",
+        short_name = "P",
+    ),
+)
+
+ci.builder(
+    name = "android-weblayer-pie-x86-wpt-smoketest",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|weblayer",
+        short_name = "P",
+    ),
+)
+
+ci.builder(
+    name = "android-webview-pie-x86-wpt-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|webview",
+        short_name = "P",
+    ),
+)
+
+ci.builder(
+    name = "android-weblayer-10-x86-rel-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|weblayer",
+        short_name = "10",
+    ),
+    triggered_by = ["android-weblayer-with-aosp-webview-x86-fyi-rel"],
+    notifies = ["weblayer-sheriff"],
+)
+
+ci.builder(
+    name = "android-weblayer-with-aosp-webview-x86-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|weblayer_with_aosp_webview",
+        short_name = "x86",
+    ),
+)
+
+ci.builder(
+    name = "Android WebView P FYI (rel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "webview",
+        short_name = "p-rel",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+# TODO(crbug.com/1022533#c40): Remove this builder once there are no associated
+# disabled tests.
+ci.builder(
+    name = "android-pie-x86-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "emulator|P|x86",
+        short_name = "rel",
+    ),
+    goma_jobs = goma.jobs.J150,
+    # Set to an empty list to avoid chromium-gitiles-trigger triggering new
+    # builds. Also we don't set any `schedule` since this builder is for
+    # reference only and should not run any new builds.
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "android-10-x86-fyi-rel-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|10",
+        short_name = "10",
+    ),
+    triggered_by = ["android-x86-fyi-rel"],
+)
+
+# TODO(crbug.com/1137474): Remove this builder once there are no associated
+# disabled tests.
+ci.builder(
+    name = "android-11-x86-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "emulator|11|x86",
+        short_name = "rel",
+    ),
+    # Set to an empty list to avoid chromium-gitiles-trigger triggering new
+    # builds. Also we don't set any `schedule` since this builder is for
+    # reference only and should not run any new builds.
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "android-12-x64-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "emulator|12|x64",
+        short_name = "rel",
+    ),
+    # Bump to 6h for now since compile on x64 seems slower than x86. It could
+    # take 3h on Android-12 (For example ci.chromium.org/b/8841892751541698720)
+    # vs 1h on Android-11 (For example ci.chromium.org/b/8841899947736889024)
+    # TODO(crbug.com/1229245): Look into ways to improve the compile time.
+    execution_timeout = 6 * time.hour,
+)
+
+ci.builder(
+    name = "android-annotator-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "network|traffic|annotations",
+        short_name = "and",
+    ),
+    notifies = ["annotator-rel"],
+)
+
+ci.builder(
+    name = "android-x86-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|x86",
+        short_name = "x86",
+    ),
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.android.star b/infra/config/subprojects/chromium/ci/chromium.android.star
new file mode 100644
index 0000000..688b5cc
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.android.star
@@ -0,0 +1,565 @@
+# 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.
+"""Definitions of builders in the chromium.android builder group."""
+
+load("//lib/builders.star", "goma", "os", "sheriff_rotations")
+load("//lib/branches.star", "branches")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.android",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    # TODO(tandrii): migrate to this gradually (current value of
+    # goma.jobs.MANY_JOBS_FOR_CI is 500).
+    # goma_jobs=goma.jobs.MANY_JOBS_FOR_CI
+    goma_jobs = goma.jobs.J150,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.ANDROID,
+)
+
+consoles.console_view(
+    name = "chromium.android",
+    branch_selector = branches.STANDARD_MILESTONE,
+    ordering = {
+        None: ["cronet", "builder", "tester"],
+        "*cpu*": ["arm", "arm64", "x86"],
+        "cronet": "*cpu*",
+        "builder": "*cpu*",
+        "builder|det": consoles.ordering(short_names = ["rel", "dbg"]),
+        "tester": ["phone", "tablet"],
+        "builder_tester|arm64": consoles.ordering(short_names = ["M proguard"]),
+    },
+)
+
+ci.builder(
+    name = "Android ASAN (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|arm",
+        short_name = "san",
+    ),
+    # Higher build timeout since dbg ASAN builds can take a while on a clobber
+    # build.
+    execution_timeout = 4 * time.hour,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "Android WebView M (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|webview",
+        short_name = "M",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Android WebView N (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|webview",
+        short_name = "N",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Android WebView O (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|webview",
+        short_name = "O",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Android WebView P (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|webview",
+        short_name = "P",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Android arm Builder (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|arm",
+        short_name = "32",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    execution_timeout = 4 * time.hour,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "Android arm64 Builder (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|arm",
+        short_name = "64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    execution_timeout = 7 * time.hour,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "Android x64 Builder (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|x86",
+        short_name = "64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    execution_timeout = 7 * time.hour,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+)
+
+ci.builder(
+    name = "Android x86 Builder (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|x86",
+        short_name = "32",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    execution_timeout = 6 * time.hour,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+)
+
+ci.builder(
+    name = "Cast Android (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "on_cq",
+        short_name = "cst",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "Deterministic Android",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|det",
+        short_name = "rel",
+    ),
+    cores = 32,
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 7 * time.hour,
+    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
+    notifies = ["Deterministic Android"],
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "Deterministic Android (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|det",
+        short_name = "dbg",
+    ),
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+    notifies = ["Deterministic Android"],
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "Marshmallow 64 bit Tester",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|phone",
+        short_name = "M",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Marshmallow Tablet Tester",
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|tablet",
+        short_name = "M",
+    ),
+    # We have limited tablet capacity and thus limited ability to run
+    # tests in parallel, hence the high timeout.
+    execution_timeout = 12 * time.hour,
+    triggered_by = ["ci/Android arm Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Nougat Phone Tester",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|phone",
+        short_name = "N",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Oreo Phone Tester",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|phone",
+        short_name = "O",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+ci.builder(
+    name = "android-10-arm64-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|arm64",
+        short_name = "10",
+    ),
+)
+
+ci.builder(
+    name = "android-arm64-proguard-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|arm64",
+        short_name = "M proguard",
+    ),
+    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
+    execution_timeout = 6 * time.hour,
+)
+
+ci.builder(
+    name = "android-bfcache-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "bfcache",
+        short_name = "bfc",
+    ),
+)
+
+ci.builder(
+    name = "android-binary-size-generator",
+    builderless = False,
+    cores = 32,
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|other",
+        short_name = "size",
+    ),
+    executable = "recipe:binary_size_generator_tot",
+    ssd = True,
+)
+
+ci.builder(
+    name = "android-cronet-arm-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|arm",
+        short_name = "dbg",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    notifies = ["cronet"],
+)
+
+ci.builder(
+    name = "android-cronet-arm-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|arm",
+        short_name = "rel",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    notifies = ["cronet"],
+)
+
+ci.builder(
+    name = "android-cronet-arm64-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|arm64",
+        short_name = "dbg",
+    ),
+    notifies = ["cronet"],
+)
+
+ci.builder(
+    name = "android-cronet-arm64-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|arm64",
+        short_name = "rel",
+    ),
+    notifies = ["cronet"],
+)
+
+ci.builder(
+    name = "android-cronet-asan-arm-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|asan",
+    ),
+    notifies = ["cronet"],
+)
+
+ci.builder(
+    name = "android-cronet-arm-rel-kitkat-tests",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test",
+        short_name = "k",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    notifies = ["cronet"],
+    triggered_by = ["ci/android-cronet-arm-rel"],
+)
+
+ci.builder(
+    name = "android-cronet-arm-rel-lollipop-tests",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test",
+        short_name = "l",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    notifies = ["cronet"],
+    triggered_by = ["ci/android-cronet-arm-rel"],
+)
+
+# Runs on a specific machine with an attached phone
+ci.builder(
+    name = "android-cronet-marshmallow-arm64-perf-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test|perf",
+        short_name = "m",
+    ),
+    cores = None,
+    cpu = None,
+    executable = "recipe:cronet",
+    notifies = ["cronet"],
+    os = os.ANDROID,
+)
+
+ci.builder(
+    name = "android-cronet-arm64-rel-marshmallow-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test",
+        short_name = "m",
+    ),
+    notifies = ["cronet"],
+    triggered_by = ["ci/android-cronet-arm64-rel"],
+)
+
+ci.builder(
+    name = "android-cronet-x86-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|x86",
+        short_name = "dbg",
+    ),
+    notifies = ["cronet"],
+)
+
+ci.builder(
+    name = "android-cronet-x86-dbg-oreo-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test",
+        short_name = "o",
+    ),
+    notifies = ["cronet"],
+    triggered_by = ["ci/android-cronet-x86-dbg"],
+)
+
+ci.builder(
+    name = "android-cronet-x86-dbg-pie-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test",
+        short_name = "p",
+    ),
+    notifies = ["cronet"],
+    triggered_by = ["ci/android-cronet-x86-dbg"],
+)
+
+ci.builder(
+    name = "android-cronet-x86-dbg-10-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test",
+        short_name = "10",
+    ),
+    notifies = ["cronet"],
+    triggered_by = ["ci/android-cronet-x86-dbg"],
+)
+
+ci.builder(
+    name = "android-cronet-x86-dbg-11-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|test",
+        short_name = "11",
+    ),
+    notifies = ["cronet"],
+    triggered_by = ["ci/android-cronet-x86-dbg"],
+)
+
+ci.builder(
+    name = "android-cronet-x86-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet|x86",
+        short_name = "rel",
+    ),
+    notifies = ["cronet"],
+)
+
+ci.builder(
+    name = "android-incremental-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|incremental",
+    ),
+)
+
+ci.builder(
+    name = "android-marshmallow-arm64-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "on_cq",
+        short_name = "M",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    execution_timeout = 4 * time.hour,
+    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "android-marshmallow-x86-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "on_cq|x86",
+        short_name = "M",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "android-marshmallow-x86-rel-non-cq",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|x86",
+        short_name = "M_non-cq",
+    ),
+)
+
+ci.builder(
+    name = "android-pie-arm64-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|phone",
+        short_name = "P",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Android arm64 Builder (dbg)"],
+)
+
+# TODO(crbug/1182468) Remove android coverage bots after coverage is
+# running on CQ.
+ci.builder(
+    name = "android-pie-arm64-coverage-experimental-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|arm64",
+        short_name = "p-cov",
+    ),
+)
+
+ci.builder(
+    name = "android-pie-arm64-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "on_cq",
+        short_name = "P",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    execution_timeout = 4 * time.hour,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "android-pie-x86-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|x86",
+        short_name = "P",
+    ),
+)
+
+ci.builder(
+    name = "android-11-x86-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder_tester|x86",
+        short_name = "11",
+    ),
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "android-weblayer-marshmallow-x86-rel-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|weblayer",
+        short_name = "M",
+    ),
+    triggered_by = ["android-weblayer-with-aosp-webview-x86-rel"],
+    notifies = ["weblayer-sheriff"],
+)
+
+ci.builder(
+    name = "android-weblayer-oreo-x86-rel-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|weblayer",
+        short_name = "O",
+    ),
+    triggered_by = ["android-weblayer-x86-rel"],
+    notifies = ["weblayer-sheriff"],
+)
+
+ci.builder(
+    name = "android-weblayer-pie-x86-rel-tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "tester|weblayer",
+        short_name = "P",
+    ),
+    triggered_by = ["android-weblayer-x86-rel"],
+    notifies = ["weblayer-sheriff"],
+)
+
+ci.builder(
+    name = "android-weblayer-with-aosp-webview-x86-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|weblayer_with_aosp_webview",
+        short_name = "x86",
+    ),
+)
+
+ci.builder(
+    name = "android-weblayer-x86-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "builder|weblayer",
+        short_name = "x86",
+    ),
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.angle.star b/infra/config/subprojects/chromium/ci/chromium.angle.star
new file mode 100644
index 0000000..a7610f98
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.angle.star
@@ -0,0 +1,252 @@
+# 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.
+"""Definitions of builders in the chromium.angle builder group."""
+
+load("//lib/builders.star", "goma", "xcode")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.angle",
+    executable = "recipe:angle_chromium",
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = ci.gpu.POOL,
+    properties = {
+        "perf_dashboard_machine_group": "ChromiumANGLE",
+    },
+    service_account = ci.gpu.SERVICE_ACCOUNT,
+    thin_tester_cores = 2,
+)
+
+consoles.console_view(
+    name = "chromium.angle",
+    ordering = {
+        None: ["Android", "Fuchsia", "Linux", "Mac", "iOS", "Windows", "Perf"],
+        "*builder*": ["Builder"],
+        "Android": "*builder*",
+        "Fuchsia": "*builder*",
+        "Linux": "*builder*",
+        "Mac": "*builder*",
+        "iOS": "*builder*",
+        "Windows": "*builder*",
+        "Perf": "*builder*",
+    },
+)
+
+ci.gpu.linux_builder(
+    name = "android-angle-arm64-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|Builder|ANGLE",
+        short_name = "arm64",
+    ),
+)
+
+ci.thin_tester(
+    name = "android-angle-arm64-nexus5x",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|Nexus5X|ANGLE",
+        short_name = "arm64",
+    ),
+    triggered_by = ["android-angle-arm64-builder"],
+)
+
+ci.gpu.linux_builder(
+    name = "android-angle-chromium-arm64-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|Builder|Chromium",
+        short_name = "arm64",
+    ),
+)
+
+ci.thin_tester(
+    name = "android-angle-chromium-arm64-nexus5x",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|Nexus5X|Chromium",
+        short_name = "arm64",
+    ),
+    triggered_by = ["android-angle-chromium-arm64-builder"],
+)
+
+ci.gpu.linux_builder(
+    name = "fuchsia-angle-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Fuchsia|Builder|ANGLE",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "linux-angle-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Builder|ANGLE",
+        short_name = "x64",
+    ),
+)
+
+ci.thin_tester(
+    name = "linux-angle-intel",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Intel|ANGLE",
+        short_name = "x64",
+    ),
+    triggered_by = ["linux-angle-builder"],
+)
+
+ci.thin_tester(
+    name = "linux-angle-nvidia",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|NVIDIA|ANGLE",
+        short_name = "x64",
+    ),
+    triggered_by = ["linux-angle-builder"],
+)
+
+ci.gpu.linux_builder(
+    name = "linux-angle-chromium-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Builder|Chromium",
+        short_name = "x64",
+    ),
+)
+
+ci.thin_tester(
+    name = "linux-angle-chromium-intel",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Intel|Chromium",
+        short_name = "x64",
+    ),
+    triggered_by = ["linux-angle-chromium-builder"],
+)
+
+ci.thin_tester(
+    name = "linux-angle-chromium-nvidia",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|NVIDIA|Chromium",
+        short_name = "x64",
+    ),
+    triggered_by = ["linux-angle-chromium-builder"],
+)
+
+ci.gpu.mac_builder(
+    name = "mac-angle-chromium-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Builder|Chromium",
+        short_name = "x64",
+    ),
+)
+
+ci.thin_tester(
+    name = "mac-angle-chromium-amd",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|AMD|Chromium",
+        short_name = "x64",
+    ),
+    triggered_by = ["mac-angle-chromium-builder"],
+)
+
+ci.thin_tester(
+    name = "mac-angle-chromium-intel",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Intel|Chromium",
+        short_name = "x64",
+    ),
+    triggered_by = ["mac-angle-chromium-builder"],
+)
+
+ci.gpu.mac_builder(
+    name = "ios-angle-builder",
+    xcode = xcode.x12d4e,
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|Builder|ANGLE",
+        short_name = "x64",
+    ),
+)
+
+ci.thin_tester(
+    name = "ios-angle-intel",
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|Intel|ANGLE",
+        short_name = "x64",
+    ),
+    triggered_by = ["ios-angle-builder"],
+)
+
+ci.gpu.windows_builder(
+    name = "win-angle-chromium-x64-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|Chromium",
+        short_name = "x64",
+    ),
+)
+
+ci.thin_tester(
+    name = "win10-angle-chromium-x64-intel",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Intel|Chromium",
+        short_name = "x64",
+    ),
+    triggered_by = ["win-angle-chromium-x64-builder"],
+)
+
+ci.thin_tester(
+    name = "win10-angle-chromium-x64-nvidia",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|NVIDIA|Chromium",
+        short_name = "x64",
+    ),
+    triggered_by = ["win-angle-chromium-x64-builder"],
+)
+
+ci.gpu.windows_builder(
+    name = "win-angle-chromium-x86-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|Chromium",
+        short_name = "x86",
+    ),
+)
+
+ci.thin_tester(
+    name = "win7-angle-chromium-x86-amd",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Win7-AMD|Chromium",
+        short_name = "x86",
+    ),
+    triggered_by = ["win-angle-chromium-x86-builder"],
+)
+
+ci.gpu.windows_builder(
+    name = "win-angle-x64-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|ANGLE",
+        short_name = "x64",
+    ),
+)
+
+ci.thin_tester(
+    name = "win7-angle-x64-nvidia",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Win7-NVIDIA|ANGLE",
+        short_name = "x64",
+    ),
+    triggered_by = ["win-angle-x64-builder"],
+)
+
+ci.thin_tester(
+    name = "win10-angle-x64-intel",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Intel|ANGLE",
+        short_name = "x64",
+    ),
+    triggered_by = ["win-angle-x64-builder"],
+)
+
+ci.thin_tester(
+    name = "win10-angle-x64-nvidia",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|NVIDIA|ANGLE",
+        short_name = "x64",
+    ),
+    triggered_by = ["win-angle-x64-builder"],
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.chromiumos.star b/infra/config/subprojects/chromium/ci/chromium.chromiumos.star
new file mode 100644
index 0000000..17be845
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.chromiumos.star
@@ -0,0 +1,329 @@
+# 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.
+"""Definitions of builders in the chromium.chromiumos builder group."""
+
+load("//lib/args.star", "args")
+load("//lib/branches.star", "branches")
+load("//lib/builder_config.star", "builder_config")
+load("//lib/builders.star", "goma", "os", "sheriff_rotations")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.chromiumos",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM,
+    tree_closing = True,
+)
+
+consoles.console_view(
+    name = "chromium.chromiumos",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    ordering = {
+        None: ["default"],
+        "default": consoles.ordering(short_names = ["ful", "rel"]),
+        "simple": ["release", "debug"],
+    },
+)
+
+ci.builder(
+    name = "linux-ash-chromium-generator-rel",
+    # This builder gets triggered against multiple branches, so it shouldn't be
+    # bootstrapped
+    bootstrap = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+    ),
+    tree_closing = False,
+    main_console_view = "main",
+    notifies = ["chrome-lacros-engprod-alerts"],
+    triggered_by = [],
+    schedule = "triggered",
+    sheriff_rotations = args.ignore_default(None),
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "cipd_archive_datas": [
+                {
+                    "yaml_files": [
+                        "gen_linux_ash_chromium_cipd_yaml_cipd.yaml",
+                    ],
+                    "refs": [
+                        "{%channel%}",
+                    ],
+                    "tags": {
+                        "version": "{%chromium_version%}",
+                    },
+                    # Because we don't run any tests.
+                    "only_set_refs_on_tests_success": False,
+                },
+            ],
+        },
+    },
+)
+
+ci.builder(
+    name = "Linux ChromiumOS Full",
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+        short_name = "ful",
+    ),
+    main_console_view = "main",
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "linux-chromiumos-full.json",
+            ],
+        },
+    },
+)
+
+ci.builder(
+    name = "chromeos-amd64-generic-asan-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|release|x64",
+        short_name = "asn",
+    ),
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "chromeos-amd64-generic-cfi-thin-lto-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|release|x64",
+        short_name = "cfi",
+    ),
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "chromeos-amd64-generic-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|debug|x64",
+        short_name = "dbg",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "chromeos-amd64-generic-lacros-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros|x64",
+        short_name = "dbg",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "chromeos-amd64-generic-rel",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|release|x64",
+        short_name = "rel",
+    ),
+    os = os.LINUX_BIONIC_REMOVE,
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "chromeos-arm-generic-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|debug",
+        short_name = "arm",
+    ),
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "chromeos-arm-generic-rel",
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_arch = builder_config.target_arch.ARM,
+            target_bits = 32,
+            target_platform = builder_config.target_platform.CHROMEOS,
+            target_cros_boards = ["arm-generic"],
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = ["chromeos"],
+        ),
+    ),
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|release",
+        short_name = "arm",
+    ),
+    os = os.LINUX_BIONIC_REMOVE,
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "chromeos-kevin-rel",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|release",
+        short_name = "kvn",
+    ),
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "linux-chromeos-annotator-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "rel",
+    ),
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "lacros-amd64-generic-binary-size-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros|size",
+    ),
+    main_console_view = "main",
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "archive_datas": [
+                # The list of files and dirs should be synched with
+                # _TRACKED_ITEMS in //build/lacros/lacros_resource_sizes.py.
+                {
+                    "files": [
+                        "chrome",
+                        "chrome_100_percent.pak",
+                        "chrome_200_percent.pak",
+                        "chrome_crashpad_handler",
+                        "headless_lib.pak",
+                        "icudtl.dat",
+                        "nacl_helper",
+                        "nacl_irt_x86_64.nexe",
+                        "resources.pak",
+                        "snapshot_blob.bin",
+                    ],
+                    "dirs": ["locales", "swiftshader"],
+                    "gcs_bucket": "chromium-lacros-fishfood",
+                    "gcs_path": "x86_64/{%position%}/lacros.zip",
+                    "archive_type": "ARCHIVE_TYPE_ZIP",
+                },
+            ],
+        },
+    },
+)
+
+ci.builder(
+    name = "lacros-amd64-generic-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros|x64",
+        short_name = "rel",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "lacros-arm-generic-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros|arm",
+        short_name = "arm",
+    ),
+    # TODO(crbug.com/1202631) Enable tree closing when stable.
+    tree_closing = False,
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "linux-chromeos-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+        short_name = "dbg",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+)
+
+ci.builder(
+    name = "linux-chromeos-rel",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+        short_name = "rel",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "linux-lacros-builder-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+        short_name = "lcr",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "linux-lacros-tester-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+        short_name = "lcr",
+    ),
+    main_console_view = "main",
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["linux-lacros-builder-rel"],
+    tree_closing = False,
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "linux-lacros-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug",
+        short_name = "lcr",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    main_console_view = "main",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+# For Chromebox for meetings(CfM)
+ci.builder(
+    name = "linux-cfm-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "simple|release",
+        short_name = "cfm",
+    ),
+    main_console_view = "main",
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.clang.star b/infra/config/subprojects/chromium/ci/chromium.clang.star
new file mode 100644
index 0000000..68edba1
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.clang.star
@@ -0,0 +1,467 @@
+# 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.
+"""Definitions of builders in the chromium.clang builder group."""
+
+load("//lib/builders.star", "goma", "os", "sheriff_rotations", "xcode")
+load("//lib/branches.star", "branches")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.clang",
+    builderless = True,
+    cores = 32,
+    executable = ci.DEFAULT_EXECUTABLE,
+    # Because these run ToT Clang, goma is not used.
+    # Naturally the runtime will be ~4-8h on average, depending on config.
+    # CFI builds will take even longer - around 11h.
+    execution_timeout = 14 * time.hour,
+    os = os.LINUX_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    properties = {
+        "perf_dashboard_machine_group": "ChromiumClang",
+    },
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM_CLANG,
+)
+
+consoles.console_view(
+    name = "chromium.clang",
+    ordering = {
+        None: [
+            "ToT Linux",
+            "ToT Android",
+            "ToT Mac",
+            "ToT Windows",
+            "ToT Code Coverage",
+        ],
+        "ToT Linux": consoles.ordering(
+            short_names = ["rel", "ofi", "dbg", "asn", "fuz", "msn", "tsn"],
+        ),
+        "ToT Android": consoles.ordering(short_names = ["rel", "dbg", "x64"]),
+        "ToT Mac": consoles.ordering(short_names = ["rel", "ofi", "dbg"]),
+        "ToT Windows": consoles.ordering(
+            short_names = ["rel", "ofi"],
+            categories = ["x64"],
+        ),
+        "ToT Windows|x64": consoles.ordering(short_names = ["rel"]),
+        "CFI|Win": consoles.ordering(short_names = ["x86", "x64"]),
+        "iOS": ["public"],
+        "iOS|public": consoles.ordering(short_names = ["sim", "dev"]),
+    },
+)
+
+[branches.console_view_entry(
+    builder = "chrome:ci/{}".format(name),
+    console_view = "chromium.clang",
+    category = category,
+    short_name = short_name,
+) for name, category, short_name in (
+    ("ToTLinuxOfficial", "ToT Linux", "ofi"),
+    ("ToTMacOfficial", "ToT Mac", "ofi"),
+    ("ToTWinOfficial", "ToT Windows", "ofi"),
+    ("ToTWinOfficial64", "ToT Windows|x64", "ofi"),
+    ("clang-tot-device", "iOS|internal", "dev"),
+)]
+
+def clang_mac_builder(*, name, cores = 24, **kwargs):
+    return ci.builder(
+        name = name,
+        cores = cores,
+        os = os.MAC_DEFAULT,
+        ssd = True,
+        properties = {
+            # The Chromium build doesn't need system Xcode, but the ToT clang
+            # bots also build clang and llvm and that build does need system
+            # Xcode.
+            "xcode_build_version": "12d4e",
+        },
+        **kwargs
+    )
+
+def clang_tot_linux_builder(short_name, category = "ToT Linux", **kwargs):
+    ci.builder(
+        console_view_entry = consoles.console_view_entry(
+            category = category,
+            short_name = short_name,
+        ),
+        notifies = [luci.notifier(
+            name = "ToT Linux notifier",
+            on_new_status = ["FAILURE"],
+            notify_emails = ["thomasanderson@chromium.org"],
+        )],
+        **kwargs
+    )
+
+ci.builder(
+    name = "CFI Linux CF",
+    goma_backend = goma.backend.RBE_PROD,
+    console_view_entry = consoles.console_view_entry(
+        category = "CFI|Linux",
+        short_name = "CF",
+    ),
+    notifies = ["CFI Linux"],
+)
+
+ci.builder(
+    name = "CFI Linux ToT",
+    console_view_entry = consoles.console_view_entry(
+        category = "CFI|Linux",
+        short_name = "ToT",
+    ),
+    notifies = ["CFI Linux"],
+)
+
+ci.builder(
+    name = "CrWinAsan",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows|Asan",
+        short_name = "asn",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "CrWinAsan(dll)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows|Asan",
+        short_name = "dll",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTAndroid",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Android",
+        short_name = "rel",
+    ),
+)
+
+ci.builder(
+    name = "ToTAndroid (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Android",
+        short_name = "dbg",
+    ),
+)
+
+ci.builder(
+    name = "ToTAndroid x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Android",
+        short_name = "x64",
+    ),
+)
+
+ci.builder(
+    name = "ToTAndroid x86",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Android",
+        short_name = "x86",
+    ),
+)
+
+ci.builder(
+    name = "ToTAndroidCoverage x86",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Code Coverage",
+        short_name = "and",
+    ),
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "ToTAndroid64",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Android",
+        short_name = "a64",
+    ),
+)
+
+ci.builder(
+    name = "ToTAndroidASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Android",
+        short_name = "asn",
+    ),
+)
+
+ci.builder(
+    name = "ToTAndroidOfficial",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Android",
+        short_name = "off",
+    ),
+)
+
+ci.builder(
+    name = "ToTChromeOS",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT ChromeOS",
+        short_name = "rel",
+    ),
+)
+
+ci.builder(
+    name = "ToTChromeOS (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT ChromeOS",
+        short_name = "dbg",
+    ),
+)
+
+ci.builder(
+    name = "ToTFuchsia x64",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "ToT Fuchsia",
+            short_name = "x64",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "misc",
+            short_name = "clang-x64",
+        ),
+    ],
+)
+
+ci.builder(
+    name = "ToTFuchsiaOfficial",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "ToT Fuchsia",
+            short_name = "off",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "misc",
+            short_name = "clang-off",
+        ),
+    ],
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinux",
+    short_name = "rel",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinux (dbg)",
+    short_name = "dbg",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinuxASan",
+    short_name = "asn",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinuxASanLibfuzzer",
+    # Requires a large disk, so has a machine specifically devoted to it
+    builderless = False,
+    short_name = "fuz",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinuxCoverage",
+    category = "ToT Code Coverage",
+    short_name = "linux",
+    executable = "recipe:chromium_clang_coverage_tot",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinuxMSan",
+    short_name = "msn",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinuxPGO",
+    short_name = "pgo",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinuxTSan",
+    short_name = "tsn",
+)
+
+clang_tot_linux_builder(
+    name = "ToTLinuxUBSanVptr",
+    short_name = "usn",
+)
+
+ci.builder(
+    name = "ToTWin",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows",
+        short_name = "rel",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWin(dbg)",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows",
+        short_name = "dbg",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWin(dll)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows",
+        short_name = "dll",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWin64",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows|x64",
+        short_name = "rel",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWin64(dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows|x64",
+        short_name = "dbg",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWin64(dll)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows|x64",
+        short_name = "dll",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWinASanLibfuzzer",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows|Asan",
+        short_name = "fuz",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWinCFI",
+    console_view_entry = consoles.console_view_entry(
+        category = "CFI|Win",
+        short_name = "x86",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWinCFI64",
+    console_view_entry = consoles.console_view_entry(
+        category = "CFI|Win",
+        short_name = "x64",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWindowsCoverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Code Coverage",
+        short_name = "win",
+    ),
+    executable = "recipe:chromium_clang_coverage_tot",
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "ToTWin64PGO",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows|x64",
+        short_name = "pgo",
+    ),
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "linux-win_cross-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Windows",
+        short_name = "lxw",
+    ),
+)
+
+ci.builder(
+    name = "ToTiOS",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|public",
+        short_name = "sim",
+    ),
+    cores = None,
+    os = os.MAC_11,
+    ssd = True,
+    xcode = xcode.x13main,
+)
+
+ci.builder(
+    name = "ToTiOSDevice",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|public",
+        short_name = "dev",
+    ),
+    cores = None,
+    os = os.MAC_11,
+    ssd = True,
+    xcode = xcode.x13main,
+)
+
+clang_mac_builder(
+    name = "ToTMac",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Mac",
+        short_name = "rel",
+    ),
+    cores = None,
+)
+
+clang_mac_builder(
+    name = "ToTMac (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Mac",
+        short_name = "dbg",
+    ),
+    cores = None,
+)
+
+clang_mac_builder(
+    name = "ToTMacASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Mac",
+        short_name = "asn",
+    ),
+    cores = None,
+)
+
+clang_mac_builder(
+    name = "ToTMacCoverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT Code Coverage",
+        short_name = "mac",
+    ),
+    executable = "recipe:chromium_clang_coverage_tot",
+    cores = None,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.dawn.star b/infra/config/subprojects/chromium/ci/chromium.dawn.star
new file mode 100644
index 0000000..e56c5a2
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.dawn.star
@@ -0,0 +1,307 @@
+# 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.
+"""Definitions of builders in the chromium.dawn builder group."""
+
+load("//lib/builders.star", "goma")
+load("//lib/branches.star", "branches")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.dawn",
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = ci.gpu.POOL,
+    service_account = ci.gpu.SERVICE_ACCOUNT,
+    thin_tester_cores = 2,
+)
+
+consoles.console_view(
+    name = "chromium.dawn",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    ordering = {
+        None: ["ToT"],
+        "*builder*": ["Builder"],
+        "*cpu*": consoles.ordering(short_names = ["x86"]),
+        "ToT|Mac": "*builder*",
+        "ToT|Windows|Builder": "*cpu*",
+        "ToT|Windows|Intel": "*cpu*",
+        "ToT|Windows|Nvidia": "*cpu*",
+        "DEPS|Mac": "*builder*",
+        "DEPS|Windows|Builder": "*cpu*",
+        "DEPS|Windows|Intel": "*cpu*",
+        "DEPS|Windows|Nvidia": "*cpu*",
+    },
+)
+
+ci.gpu.linux_builder(
+    name = "Dawn Linux x64 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Linux|Builder",
+        short_name = "x64",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "Dawn Linux x64 DEPS Builder",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Linux|Builder",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.thin_tester(
+    name = "Dawn Linux x64 DEPS Release (Intel HD 630)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Linux|Intel",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Linux x64 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Linux x64 DEPS Release (NVIDIA)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Linux|Nvidia",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Linux x64 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Linux x64 Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Linux|Intel",
+        short_name = "x64",
+    ),
+    triggered_by = ["Dawn Linux x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Linux x64 Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Linux|Nvidia",
+        short_name = "x64",
+    ),
+    triggered_by = ["Dawn Linux x64 Builder"],
+)
+
+ci.gpu.mac_builder(
+    name = "Dawn Mac x64 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Mac|Builder",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.mac_builder(
+    name = "Dawn Mac x64 DEPS Builder",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Mac|Builder",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+# Note that the Mac testers are all thin Linux VMs, triggering jobs on the
+# physical Mac hardware in the Swarming pool which is why they run on linux
+ci.thin_tester(
+    name = "Dawn Mac x64 DEPS Release (AMD)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Mac|AMD",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Mac x64 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Mac x64 DEPS Release (Intel)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Mac|Intel",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Mac x64 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Mac x64 Experimental Release (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Mac|AMD",
+        short_name = "exp",
+    ),
+    triggered_by = ["Dawn Mac x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Mac x64 Experimental Release (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Mac|Intel",
+        short_name = "exp",
+    ),
+    triggered_by = ["Dawn Mac x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Mac x64 Release (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Mac|AMD",
+        short_name = "x64",
+    ),
+    triggered_by = ["Dawn Mac x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Mac x64 Release (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Mac|Intel",
+        short_name = "x64",
+    ),
+    triggered_by = ["Dawn Mac x64 Builder"],
+)
+
+ci.gpu.windows_builder(
+    name = "Dawn Win10 x64 ASAN Release",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|ASAN",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "Dawn Win10 x64 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|Builder",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "Dawn Win10 x64 DEPS Builder",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows|Builder",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+# Note that the Win testers are all thin Linux VMs, triggering jobs on the
+# physical Win hardware in the Swarming pool, which is why they run on linux
+ci.thin_tester(
+    name = "Dawn Win10 x64 DEPS Release (Intel HD 630)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows|Intel",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Win10 x64 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Win10 x64 DEPS Release (NVIDIA)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows|Nvidia",
+        short_name = "x64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Win10 x64 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Win10 x64 Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|Intel",
+        short_name = "x64",
+    ),
+    triggered_by = ["Dawn Win10 x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Win10 x64 Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|Nvidia",
+        short_name = "x64",
+    ),
+    triggered_by = ["Dawn Win10 x64 Builder"],
+)
+
+ci.gpu.windows_builder(
+    name = "Dawn Win10 x86 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|Builder",
+        short_name = "x86",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "Dawn Win10 x86 DEPS Builder",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows|Builder",
+        short_name = "x86",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+# Note that the Win testers are all thin Linux VMs, triggering jobs on the
+# physical Win hardware in the Swarming pool, which is why they run on linux
+ci.thin_tester(
+    name = "Dawn Win10 x86 DEPS Release (Intel HD 630)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows|Intel",
+        short_name = "x86",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Win10 x86 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Win10 x86 DEPS Release (NVIDIA)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows|Nvidia",
+        short_name = "x86",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Dawn Win10 x86 DEPS Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Win10 x86 Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|Intel",
+        short_name = "x86",
+    ),
+    triggered_by = ["Dawn Win10 x86 Builder"],
+)
+
+ci.thin_tester(
+    name = "Dawn Win10 x86 Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT|Windows|Nvidia",
+        short_name = "x86",
+    ),
+    triggered_by = ["Dawn Win10 x86 Builder"],
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.fuzz.star b/infra/config/subprojects/chromium/ci/chromium.fuzz.star
new file mode 100644
index 0000000..cf9b751
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.fuzz.star
@@ -0,0 +1,462 @@
+# 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.
+"""Definitions of builders in the chromium.fuzz builder group."""
+
+load("//lib/builders.star", "goma", "os", "xcode")
+load("//lib/ci.star", "ci", "rbe_instance")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.fuzz",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    notifies = ["chromesec-lkgr-failures"],
+    os = os.LINUX_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.console_view(
+    name = "chromium.fuzz",
+    ordering = {
+        None: [
+            "afl",
+            "win asan",
+            "mac asan",
+            "cros asan",
+            "linux asan",
+            "libfuzz",
+            "linux msan",
+            "linux tsan",
+        ],
+        "*config*": consoles.ordering(short_names = ["dbg", "rel"]),
+        "win asan": "*config*",
+        "mac asan": "*config*",
+        "linux asan": "*config*",
+        "linux asan|x64 v8-ARM": "*config*",
+        "libfuzz": consoles.ordering(short_names = [
+            "chromeos-asan",
+            "linux32",
+            "linux32-dbg",
+            "linux",
+            "linux-dbg",
+            "linux-msan",
+            "linux-ubsan",
+            "mac-asan",
+            "win-asan",
+        ]),
+    },
+)
+
+ci.builder(
+    name = "ASAN Debug",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux asan",
+        short_name = "dbg",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "ASan Debug (32-bit x86 with V8-ARM)",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux asan|x64 v8-ARM",
+        short_name = "dbg",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+)
+
+ci.builder(
+    name = "ASAN Release",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux asan",
+        short_name = "rel",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 5,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "ASan Release (32-bit x86 with V8-ARM)",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux asan|x64 v8-ARM",
+        short_name = "rel",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+)
+
+ci.builder(
+    name = "ASAN Release Media",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux asan",
+        short_name = "med",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Afl Upload Linux ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "afl",
+        short_name = "afl",
+    ),
+    executable = "recipe:chromium_afl",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+)
+
+ci.builder(
+    name = "ASan Release Media (32-bit x86 with V8-ARM)",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux asan|x64 v8-ARM",
+        short_name = "med",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+)
+
+ci.builder(
+    name = "ChromiumOS ASAN Release",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros asan",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 6,
+    ),
+)
+
+ci.builder(
+    name = "MSAN Release (chained origins)",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux msan",
+        short_name = "org",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "MSAN Release (no origins)",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux msan",
+        short_name = "rel",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Mac ASAN Release",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "mac asan",
+        short_name = "rel",
+    ),
+    cores = 4,
+    os = os.MAC_DEFAULT,
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 2,
+    ),
+)
+
+ci.builder(
+    name = "Mac ASAN Release Media",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "mac asan",
+        short_name = "med",
+    ),
+    cores = 4,
+    os = os.MAC_DEFAULT,
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 2,
+    ),
+)
+
+ci.builder(
+    name = "TSAN Debug",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux tsan",
+        short_name = "dbg",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "TSAN Release",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux tsan",
+        short_name = "rel",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 3,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "UBSan Release",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux UBSan",
+        short_name = "rel",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "UBSan vptr Release",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux UBSan",
+        short_name = "vpt",
+    ),
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 4,
+    ),
+    goma_backend = None,
+    reclient_jobs = 250,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Win ASan Release",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "win asan",
+        short_name = "rel",
+    ),
+    os = os.WINDOWS_DEFAULT,
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 7,
+    ),
+)
+
+ci.builder(
+    name = "Win ASan Release Media",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "win asan",
+        short_name = "med",
+    ),
+    os = os.WINDOWS_DEFAULT,
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 6,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Chrome OS ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "chromeos-asan",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 3,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload iOS Catalyst Debug",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "ios",
+    ),
+    cores = 4,
+    executable = "recipe:chromium_libfuzzer",
+    execution_timeout = 4 * time.hour,
+    os = os.MAC_11,
+    xcode = xcode.x13main,
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "linux",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 5,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux ASan Debug",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "linux-dbg",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 5,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux MSan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "linux-msan",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 5,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux UBSan",
+    # Do not use builderless for this (crbug.com/980080).
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "linux-ubsan",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    execution_timeout = 4 * time.hour,
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 5,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux V8-ARM64 ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "arm64",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 1,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux V8-ARM64 ASan Debug",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "arm64-dbg",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 1,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux32 ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "linux32",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 3,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux32 ASan Debug",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "linux32-dbg",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 3,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux32 V8-ARM ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "arm",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 1,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Linux32 V8-ARM ASan Debug",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "arm-dbg",
+    ),
+    executable = "recipe:chromium_libfuzzer",
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 1,
+    ),
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Mac ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "mac-asan",
+    ),
+    cores = 24,
+    executable = "recipe:chromium_libfuzzer",
+    execution_timeout = 4 * time.hour,
+    os = os.MAC_DEFAULT,
+)
+
+ci.builder(
+    name = "Libfuzzer Upload Windows ASan",
+    console_view_entry = consoles.console_view_entry(
+        category = "libfuzz",
+        short_name = "win-asan",
+    ),
+    # crbug.com/1175182: Temporarily increase timeout
+    execution_timeout = 4 * time.hour,
+    executable = "recipe:chromium_libfuzzer",
+    os = os.WINDOWS_DEFAULT,
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 3,
+    ),
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.fyi.star b/infra/config/subprojects/chromium/ci/chromium.fyi.star
new file mode 100644
index 0000000..94d6deed3
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.fyi.star
@@ -0,0 +1,1122 @@
+# 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.
+"""Definitions of builders in the chromium.fuzz builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builder_config.star", "builder_config")
+load("//lib/builders.star", "cpu", "goma", "os", "xcode")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.fyi",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = 10 * time.hour,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.console_view(
+    name = "chromium.fyi",
+    branch_selector = branches.STANDARD_MILESTONE,
+    ordering = {
+        None: [
+            "code_coverage",
+            "cronet",
+            "mac",
+            "deterministic",
+            "fuchsia",
+            "chromeos",
+            "iOS",
+            "infra",
+            "linux",
+            "recipe",
+            "site_isolation",
+            "network",
+            "viz",
+            "win10",
+            "win32",
+            "paeverywhere",
+            "backuprefptr",
+        ],
+        "code_coverage": consoles.ordering(
+            short_names = ["and", "ann", "lnx", "lcr", "jcr", "mac"],
+        ),
+        "mac": consoles.ordering(short_names = ["bld", "15", "herm"]),
+        "deterministic|mac": consoles.ordering(short_names = ["rel", "dbg"]),
+        "iOS|iOS13": consoles.ordering(short_names = ["dev", "sim"]),
+        "linux|blink": consoles.ordering(short_names = ["TD"]),
+    },
+)
+
+def fyi_celab_builder(*, name, **kwargs):
+    kwargs.setdefault("executable", "recipe:celab")
+    kwargs.setdefault("execution_timeout", ci.DEFAULT_EXECUTION_TIMEOUT)
+    kwargs.setdefault("os", os.WINDOWS_ANY)
+    kwargs.setdefault("properties", {
+        "exclude": "chrome_only",
+        "pool_name": "celab-chromium-ci",
+        "pool_size": 20,
+        "tests": "*",
+    })
+    return ci.builder(name = name, **kwargs)
+
+def fyi_coverage_builder(*, name, **kwargs):
+    kwargs.setdefault("cores", 32)
+    kwargs.setdefault("execution_timeout", 20 * time.hour)
+    kwargs.setdefault("ssd", True)
+    return ci.builder(name = name, **kwargs)
+
+def fyi_ios_builder(*, name, **kwargs):
+    kwargs.setdefault("cores", None)
+    kwargs.setdefault("os", os.MAC_11)
+    kwargs.setdefault("xcode", xcode.x13main)
+    return ci.builder(name = name, **kwargs)
+
+def fyi_mac_builder(*, name, **kwargs):
+    kwargs.setdefault("cores", 4)
+    kwargs.setdefault("os", os.MAC_DEFAULT)
+    return ci.builder(name = name, **kwargs)
+
+ci.builder(
+    name = "Linux Viz",
+    console_view_entry = consoles.console_view_entry(
+        category = "viz",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "Site Isolation Android",
+    console_view_entry = consoles.console_view_entry(
+        category = "site_isolation",
+    ),
+    notifies = ["Site Isolation Android"],
+    goma_backend = None,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "VR Linux",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "android-backuprefptr-arm-fyi-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "backuprefptr|android",
+        short_name = "32rel",
+    ),
+    notifies = ["chrome-memory-safety"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "android-backuprefptr-arm64-fyi-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "backuprefptr|android",
+        short_name = "64rel",
+    ),
+    notifies = ["chrome-memory-safety"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "fuchsia-fyi-arm64-dbg",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|a64",
+            short_name = "dbg",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "fyi",
+            short_name = "a64-dbg",
+        ),
+    ],
+    notifies = ["cr-fuchsia"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "fuchsia-fyi-arm64-femu",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|a64",
+            short_name = "femu",
+        ),
+    ],
+    notifies = ["cr-fuchsia"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "fuchsia-fyi-arm64-rel",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|a64",
+            short_name = "rel",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "fyi",
+            short_name = "a64",
+        ),
+    ],
+    notifies = ["cr-fuchsia"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "fuchsia-fyi-x64-dbg",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|x64",
+            short_name = "dbg",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "fyi",
+            short_name = "x64-dbg",
+        ),
+    ],
+    notifies = ["cr-fuchsia"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "fuchsia-fyi-x64-rel",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|x64",
+            short_name = "rel",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "fyi",
+            short_name = "x64",
+        ),
+    ],
+    notifies = ["cr-fuchsia"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "lacros-amd64-generic-rel-fyi",
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros",
+        short_name = "lcr",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-annotator-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "network|traffic|annotations",
+        short_name = "lnx",
+    ),
+    notifies = ["annotator-rel"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-ash-chromium-builder-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+        short_name = "lcr",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "archive_datas": [
+                {
+                    "files": [
+                        "chrome",
+                        "chrome_100_percent.pak",
+                        "chrome_200_percent.pak",
+                        "chrome_crashpad_handler",
+                        "headless_lib.pak",
+                        "icudtl.dat",
+                        "libminigbm.so",
+                        "nacl_helper",
+                        "nacl_irt_x86_64.nexe",
+                        "resources.pak",
+                        "snapshot_blob.bin",
+                        "test_ash_chrome",
+                    ],
+                    "dirs": ["locales", "swiftshader"],
+                    "gcs_bucket": "ash-chromium-on-linux-prebuilts",
+                    "gcs_path": "x86_64/{%position%}/ash-chromium.zip",
+                    "archive_type": "ARCHIVE_TYPE_ZIP",
+                    "latest_upload": {
+                        "gcs_path": "x86_64/latest/ash-chromium.txt",
+                        "gcs_file_content": "{%position%}",
+                    },
+                },
+            ],
+        },
+    },
+)
+
+ci.builder(
+    name = "linux-lacros-version-skew-fyi",
+    console_view_entry = consoles.console_view_entry(
+        category = "default",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-blink-animation-use-time-delta",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|blink",
+        short_name = "TD",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-blink-heap-concurrent-marking-tsan-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|blink",
+        short_name = "CM",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-blink-heap-verification",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|blink",
+        short_name = "VF",
+    ),
+    notifies = ["linux-blink-fyi-bots"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-blink-v8-oilpan",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|blink",
+        short_name = "VO",
+    ),
+    notifies = ["linux-blink-fyi-bots"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-example-builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    schedule = "with 12h interval",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "linux-fieldtrial-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-lacros-builder-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-lacros-tester-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    triggered_by = ["linux-lacros-builder-fyi-rel"],
+)
+
+ci.builder(
+    name = "linux-lacros-dbg-fyi",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-lacros-dbg-tests-fyi",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    triggered_by = ["linux-lacros-dbg-fyi"],
+)
+
+ci.builder(
+    name = "linux-backuprefptr-x64-fyi-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "backuprefptr|linux",
+        short_name = "64rel",
+    ),
+    notifies = ["chrome-memory-safety"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-perfetto-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-wpt-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    experimental = True,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-wpt-identity-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    experimental = True,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-wpt-input-fyi-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+    ),
+    experimental = True,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+# This is launching & collecting entirely isolated tests.
+# OS shouldn't matter.
+ci.builder(
+    name = "mac-osxbeta-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "beta",
+    ),
+    goma_backend = goma.backend.RBE_PROD,
+    main_console_view = None,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    triggered_by = ["ci/Mac Builder"],
+)
+
+ci.builder(
+    name = "linux-headless-shell-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+        short_name = "hdls",
+    ),
+    notifies = ["headless-owners"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "mac-paeverywhere-x64-fyi-dbg",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "paeverywhere|mac",
+        short_name = "64dbg",
+    ),
+    cores = None,
+    notifies = ["chrome-memory-safety"],
+    os = os.MAC_ANY,
+)
+
+ci.builder(
+    name = "mac-paeverywhere-x64-fyi-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "paeverywhere|mac",
+        short_name = "64rel",
+    ),
+    cores = None,
+    notifies = ["chrome-memory-safety"],
+    os = os.MAC_ANY,
+)
+
+ci.builder(
+    name = "win-backuprefptr-x86-fyi-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "backuprefptr|win",
+        short_name = "32rel",
+    ),
+    notifies = ["chrome-memory-safety"],
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "win-backuprefptr-x64-fyi-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "backuprefptr|win",
+        short_name = "64rel",
+    ),
+    notifies = ["chrome-memory-safety"],
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "win-pixel-builder-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "win10",
+    ),
+    os = os.WINDOWS_10,
+)
+
+ci.builder(
+    name = "win-pixel-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "win10",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    triggered_by = ["win-pixel-builder-rel"],
+)
+
+ci.builder(
+    name = "linux-upload-perfetto",
+    console_view_entry = consoles.console_view_entry(
+        category = "perfetto",
+        short_name = "lnx",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "mac-upload-perfetto",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "perfetto",
+        short_name = "mac",
+    ),
+    cores = None,
+    os = os.MAC_DEFAULT,
+    schedule = "with 3h interval",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "win-upload-perfetto",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "perfetto",
+        short_name = "win",
+    ),
+    os = os.WINDOWS_DEFAULT,
+    schedule = "with 3h interval",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "Comparison Linux",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+        short_name = "cmp",
+    ),
+    goma_jobs = 250,
+    executable = "recipe:reclient_goma_comparison",
+    execution_timeout = 6 * time.hour,
+    reclient_cache_silo = "Comparison Linux - cache siloed",
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = 250,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "Linux Builder (j-500) (reclient)",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+        short_name = "re",
+    ),
+    goma_backend = None,
+    reclient_rewrapper_env = {
+        "RBE_platform": "container-image=docker://gcr.io/cloud-marketplace/google/rbe-ubuntu16-04@sha256:b4dad0bfc4951d619229ab15343a311f2415a16ef83bcaa55b44f4e2bf1cf635,pool=linux-e2-custom_0",
+    },
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_jobs = 500,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    schedule = "triggered",
+)
+
+# Start - Reclient migration, phase 2, block 1 shadow builders
+ci.builder(
+    name = "Linux CFI (reclient shadow)",
+    console_view_entry = consoles.console_view_entry(
+        category = "cfi",
+        short_name = "lnx",
+    ),
+    cores = 32,
+    # TODO(thakis): Remove once https://crbug.com/927738 is resolved.
+    execution_timeout = 5 * time.hour,
+    goma_backend = None,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    reclient_jobs = 400,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+# End - Reclient migration, phase 2, block 1 shadow builders
+
+ci.builder(
+    name = "Win x64 Builder (reclient)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "win",
+        short_name = "re",
+    ),
+    cores = 32,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "Win x64 Builder (reclient compare)",
+    builderless = True,
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+            apply_configs = ["use_clang_coverage", "enable_reclient", "reclient_test"],
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "win",
+        short_name = "re",
+    ),
+    cores = 32,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_rewrapper_env = {"RBE_compare": "true"},
+    reclient_ensure_verified = True,
+    description_html = "verify artifacts. should be removed after the migration. crbug.com/1260232",
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "Win x64 Builder (reclient)(cross)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "win",
+        short_name = "re x",
+    ),
+    cores = 32,
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_profiler_service = "reclient-win",
+    reclient_publish_trace = True,
+    os = os.WINDOWS_DEFAULT,
+)
+
+fyi_mac_builder(
+    name = "Mac Builder (reclient)",
+    builderless = True,
+    cores = None,  # crbug.com/1245114
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "re",
+    ),
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    description_html = "experiment reclient on mac. should be removed after the migration. crbug.com/1244441",
+)
+
+fyi_mac_builder(
+    name = "Mac Builder (reclient compare)",
+    builderless = True,
+    cores = None,  # crbug.com/1245114
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "cmp",
+    ),
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    reclient_rewrapper_env = {"RBE_compare": "true"},
+    reclient_ensure_verified = True,
+    description_html = "verify artifacts. should be removed after the migration. crbug.com/1260232",
+)
+
+fyi_mac_builder(
+    name = "mac-arm64-on-arm64-rel-reclient",
+
+    # same with mac-arm64-on-arm64-rel
+    cores = None,  # crbug.com/1245114
+    cpu = cpu.ARM64,
+    os = os.MAC_11,
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "re",
+    ),
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    description_html = "experiment reclient on mac-arm. should be removed after the migration. crbug.com/1252626",
+)
+
+ci.builder(
+    name = "chromeos-amd64-generic-rel (goma cache silo)",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros x64",
+        short_name = "cgc",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "chromeos-amd64-generic-rel (reclient)",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros x64",
+    ),
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    reclient_rewrapper_env = {"RBE_cache_silo": "chromeos-amd64-generic-rel (reclient)"},
+)
+
+# TODO(crbug.com/1235218): remove after the migration.
+ci.builder(
+    name = "chromeos-amd64-generic-rel (reclient compare)",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros x64",
+        short_name = "cmp",
+    ),
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    reclient_rewrapper_env = {"RBE_compare": "true"},
+    reclient_ensure_verified = True,
+    description_html = "verify artifacts. should be removed after the migration. crbug.com/1235218",
+)
+
+ci.builder(
+    name = "lacros-amd64-generic-rel (goma cache silo)",
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros x64",
+        short_name = "cgc",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "lacros-amd64-generic-rel (reclient)",
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros x64",
+    ),
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    reclient_rewrapper_env = {"RBE_cache_silo": "lacros-amd64-generic-rel (reclient)"},
+)
+
+ci.builder(
+    name = "linux-lacros-builder-rel (goma cache silo)",
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros rel",
+        short_name = "cgc",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-lacros-builder-rel (reclient)",
+    console_view_entry = consoles.console_view_entry(
+        category = "lacros rel",
+    ),
+    goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    reclient_rewrapper_env = {"RBE_cache_silo": "linux-lacros-builder-rel (reclient)"},
+)
+
+fyi_celab_builder(
+    name = "win-celab-builder-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "celab",
+    ),
+    schedule = "0 0,6,12,18 * * *",
+    triggered_by = [],
+)
+
+fyi_celab_builder(
+    name = "win-celab-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "celab",
+    ),
+    triggered_by = ["win-celab-builder-rel"],
+)
+
+fyi_coverage_builder(
+    name = "android-code-coverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "and",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    coverage_test_types = ["overall", "unit"],
+    schedule = "triggered",
+    triggered_by = [],
+    use_java_coverage = True,
+)
+
+fyi_coverage_builder(
+    name = "android-code-coverage-native",
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "ann",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    use_clang_coverage = True,
+    coverage_test_types = ["overall", "unit"],
+)
+
+fyi_coverage_builder(
+    name = "fuchsia-code-coverage",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "code_coverage",
+            short_name = "fsa",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "misc",
+            short_name = "cov",
+        ),
+    ],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    use_clang_coverage = True,
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+fyi_coverage_builder(
+    name = "ios-simulator-code-coverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "ios",
+    ),
+    cores = None,
+    os = os.MAC_11,
+    use_clang_coverage = True,
+    coverage_exclude_sources = "ios_test_files_and_test_utils",
+    coverage_test_types = ["overall", "unit"],
+    xcode = xcode.x13main,
+)
+
+fyi_coverage_builder(
+    name = "linux-chromeos-code-coverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "lcr",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    use_clang_coverage = True,
+    coverage_test_types = ["overall", "unit"],
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+fyi_coverage_builder(
+    name = "linux-chromeos-js-code-coverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "jcr",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    use_javascript_coverage = True,
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+fyi_coverage_builder(
+    name = "linux-code-coverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "lnx",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    use_clang_coverage = True,
+    coverage_test_types = ["overall", "unit"],
+    triggered_by = [],
+)
+
+fyi_coverage_builder(
+    name = "linux-lacros-code-coverage",
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "lac",
+    ),
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    use_clang_coverage = True,
+    coverage_test_types = ["overall", "unit"],
+)
+
+fyi_coverage_builder(
+    name = "mac-code-coverage",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "mac",
+    ),
+    cores = 24,
+    os = os.MAC_ANY,
+    coverage_test_types = ["overall", "unit"],
+    use_clang_coverage = True,
+)
+
+fyi_coverage_builder(
+    name = "win10-code-coverage",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "code_coverage",
+        short_name = "win",
+    ),
+    os = os.WINDOWS_DEFAULT,
+    coverage_test_types = ["overall", "unit"],
+    use_clang_coverage = True,
+)
+
+fyi_ios_builder(
+    name = "ios-asan",
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS",
+        short_name = "asan",
+    ),
+)
+fyi_ios_builder(
+    name = "ios-catalyst",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "iOS",
+            short_name = "ctl",
+        ),
+    ],
+    os = os.MAC_11,
+)
+fyi_ios_builder(
+    name = "ios-reclient",
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS",
+        short_name = "re",
+    ),
+    # Because of an error in the wrapper function implementation, this value was
+    # not modifying the config. The goma property should have no effect if the
+    # GN args to use goma isn't set, so commenting this out to avoid modifying
+    # the generated config during the freeze.
+    # goma_backend = None,
+    reclient_instance = rbe_instance.DEFAULT,
+    description_html = "experiment reclient for ios. remove after the migration. crbug.com/1254986",
+)
+
+fyi_ios_builder(
+    name = "ios-simulator-cronet",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cronet",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    notifies = ["cronet"],
+)
+
+fyi_ios_builder(
+    name = "ios-simulator-multi-window",
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS",
+        short_name = "mwd",
+    ),
+)
+
+fyi_ios_builder(
+    name = "ios-webkit-tot",
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS",
+        short_name = "wk",
+    ),
+    schedule = "0 1-23/6 * * *",
+    triggered_by = [],
+    xcode = xcode.x13wk,
+)
+
+fyi_ios_builder(
+    name = "ios14-beta-simulator",
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|iOS14",
+        short_name = "ios14",
+    ),
+    os = os.MAC_11,
+    schedule = "0 0,4,8,12,16,20 * * *",
+    triggered_by = [],
+)
+
+fyi_ios_builder(
+    name = "ios14-sdk-simulator",
+    console_view_entry = consoles.console_view_entry(
+        category = "iOS|iOS14",
+        short_name = "sdk14",
+    ),
+    os = os.MAC_11,
+    cpu = cpu.ARM64,
+    schedule = "0 2,6,10,14,18,22 * * *",
+    triggered_by = [],
+)
+
+fyi_ios_builder(
+    name = "ios15-beta-simulator",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "iOS|iOS15",
+            short_name = "ios15",
+        ),
+    ],
+    os = os.MAC_11,
+)
+
+fyi_ios_builder(
+    name = "ios15-sdk-device",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "iOS|iOS15",
+            short_name = "dev",
+        ),
+    ],
+    os = os.MAC_11,
+    xcode = xcode.x13latestbeta,
+)
+
+fyi_ios_builder(
+    name = "ios15-sdk-simulator",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "iOS|iOS15",
+            short_name = "sdk15",
+        ),
+    ],
+    os = os.MAC_11,
+    xcode = xcode.x13latestbeta,
+)
+
+fyi_mac_builder(
+    name = "Mac Builder Next",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "bld",
+    ),
+    cores = None,
+    os = None,
+)
+
+fyi_mac_builder(
+    name = "Mac deterministic",
+    console_view_entry = consoles.console_view_entry(
+        category = "deterministic|mac",
+        short_name = "rel",
+    ),
+    cores = None,
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+)
+
+fyi_mac_builder(
+    name = "Mac deterministic (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "deterministic|mac",
+        short_name = "dbg",
+    ),
+    cores = None,
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+    os = os.MAC_DEFAULT,
+)
+
+fyi_mac_builder(
+    name = "mac-hermetic-upgrade-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "herm",
+    ),
+    cores = 12,
+)
+
+ci.builder(
+    name = "Win 10 Fast Ring",
+    console_view_entry = consoles.console_view_entry(
+        category = "win10",
+    ),
+    os = os.WINDOWS_10,
+    notifies = ["Win 10 Fast Ring"],
+)
+
+ci.builder(
+    name = "Win10 Tests x64 20h2",
+    console_view_entry = consoles.console_view_entry(
+        category = "win10|20h2",
+    ),
+    goma_backend = None,
+    main_console_view = None,
+    os = os.WINDOWS_10,
+    triggered_by = ["ci/Win x64 Builder"],
+)
+
+ci.builder(
+    name = "win32-arm64-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "win32|arm64",
+    ),
+    cpu = cpu.X86,
+    goma_jobs = goma.jobs.J150,
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win-annotator-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "network|traffic|annotations",
+        short_name = "win",
+    ),
+    execution_timeout = 16 * time.hour,
+    notifies = ["annotator-rel"],
+    os = os.WINDOWS_DEFAULT,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
new file mode 100644
index 0000000..ba06a0e
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.fyi.star
@@ -0,0 +1,630 @@
+# 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.
+"""Definitions of builders in the chromium.gpu.fyi builder group."""
+
+load("//lib/builders.star", "goma", "sheriff_rotations")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.gpu.fyi",
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = 6 * time.hour,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = ci.gpu.POOL,
+    properties = {
+        "perf_dashboard_machine_group": "ChromiumGPUFYI",
+    },
+    service_account = ci.gpu.SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM_GPU,
+    thin_tester_cores = 2,
+)
+
+consoles.console_view(
+    name = "chromium.gpu.fyi",
+    ordering = {
+        None: ["Windows", "Mac", "Linux"],
+        "*builder*": ["Builder"],
+        "*type*": consoles.ordering(short_names = ["rel", "dbg", "exp"]),
+        "*cpu*": consoles.ordering(short_names = ["x86"]),
+        "Windows": "*builder*",
+        "Windows|Builder": ["Release", "dx12vk", "Debug"],
+        "Windows|Builder|Release": "*cpu*",
+        "Windows|Builder|dx12vk": "*type*",
+        "Windows|Builder|Debug": "*cpu*",
+        "Windows|10|x64|Intel": "*type*",
+        "Windows|10|x64|Nvidia": "*type*",
+        "Windows|10|x86|Nvidia": "*type*",
+        "Windows|7|x64|Nvidia": "*type*",
+        "Mac": "*builder*",
+        "Mac|Builder": "*type*",
+        "Mac|AMD|Retina": "*type*",
+        "Mac|Intel": "*type*",
+        "Mac|Nvidia": "*type*",
+        "Linux": "*builder*",
+        "Linux|Builder": "*type*",
+        "Linux|Intel": "*type*",
+        "Linux|Nvidia": "*type*",
+        "Android": ["L32", "M64", "N64", "P32", "vk", "skgl", "skv"],
+        "Android|M64": ["QCOM"],
+    },
+)
+
+def gpu_fyi_windows_builder(*, name, **kwargs):
+    kwargs.setdefault("execution_timeout", ci.DEFAULT_EXECUTION_TIMEOUT)
+    return ci.gpu.windows_builder(name = name, **kwargs)
+
+ci.gpu.linux_builder(
+    name = "Android FYI Release (NVIDIA Shield TV)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|N64|NVDA",
+        short_name = "STV",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "Android FYI Release (Nexus 5)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|L32",
+        short_name = "N5",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "Android FYI Release (Nexus 5X)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|M64|QCOM",
+        short_name = "N5X",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "Android FYI Release (Nexus 9)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|M64|NVDA",
+        short_name = "N9",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "Android FYI Release (Pixel 2)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|P32|QCOM",
+        short_name = "P2",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "Android FYI Release (Pixel 4)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|R32|QCOM",
+        short_name = "P4",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "Android FYI SkiaRenderer GL (Nexus 5X)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|skgl|M64",
+        short_name = "N5X",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "Android FYI SkiaRenderer Vulkan (Pixel 2)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Android|skv|P32",
+        short_name = "P2",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "ChromeOS FYI Release (amd64-generic)",
+    # Runs a lot of tests + VMs are slower than real hardware, so increase the
+    # timeout.
+    execution_timeout = 8 * time.hour,
+    console_view_entry = consoles.console_view_entry(
+        category = "ChromeOS|amd64|generic",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "ChromeOS FYI Release (kevin)",
+    console_view_entry = consoles.console_view_entry(
+        category = "ChromeOS|arm|kevin",
+        short_name = "kvn",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "GPU FYI Lacros x64 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Lacros|Builder",
+        short_name = "rel",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "GPU FYI Linux Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Builder",
+        short_name = "rel",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "GPU FYI Linux Builder (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Builder",
+        short_name = "dbg",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "Linux FYI GPU TSAN Release",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux",
+        short_name = "tsn",
+    ),
+)
+
+# Builder + tester.
+ci.gpu.linux_builder(
+    name = "Linux FYI SkiaRenderer Dawn Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Intel",
+        short_name = "skd",
+    ),
+)
+
+ci.gpu.mac_builder(
+    name = "GPU FYI Mac Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Builder",
+        short_name = "rel",
+    ),
+)
+
+ci.gpu.mac_builder(
+    name = "GPU FYI Mac Builder (asan)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Builder",
+        short_name = "asn",
+    ),
+)
+
+ci.gpu.mac_builder(
+    name = "GPU FYI Mac Builder (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Builder",
+        short_name = "dbg",
+    ),
+)
+
+ci.gpu.mac_builder(
+    name = "GPU FYI Mac arm64 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Builder",
+        short_name = "arm",
+    ),
+)
+
+ci.thin_tester(
+    name = "Lacros FYI x64 Release (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Lacros|AMD",
+        short_name = "amd",
+    ),
+    triggered_by = ["GPU FYI Lacros x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Lacros FYI x64 Release (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Lacros|Intel",
+        short_name = "int",
+    ),
+    triggered_by = ["GPU FYI Lacros x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI Debug (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Nvidia",
+        short_name = "dbg",
+    ),
+    triggered_by = ["GPU FYI Linux Builder (dbg)"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI Experimental Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Intel",
+        short_name = "exp",
+    ),
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI Experimental Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Nvidia",
+        short_name = "exp",
+    ),
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Nvidia",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI Release (AMD RX 5500 XT)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|AMD",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Intel",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI Release (Intel UHD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Intel",
+        short_name = "uhd",
+    ),
+    # TODO(https://crbug.com/986939): Remove this increased timeout once more
+    # devices are added.
+    execution_timeout = 18 * time.hour,
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI SkiaRenderer Vulkan (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Intel",
+        short_name = "skv",
+    ),
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Linux FYI SkiaRenderer Vulkan (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux|Nvidia",
+        short_name = "skv",
+    ),
+    triggered_by = ["GPU FYI Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Debug (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Intel",
+        short_name = "dbg",
+    ),
+    triggered_by = ["GPU FYI Mac Builder (dbg)"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Experimental Release (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Intel",
+        short_name = "exp",
+    ),
+    triggered_by = ["GPU FYI Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Experimental Retina Release (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|AMD|Retina",
+        short_name = "exp",
+    ),
+    triggered_by = ["GPU FYI Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Experimental Retina Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Nvidia",
+        short_name = "exp",
+    ),
+    # This bot has one machine backing its tests at the moment.
+    # If it gets more, this can be removed.
+    # See crbug.com/853307 for more context.
+    execution_timeout = 12 * time.hour,
+    triggered_by = ["GPU FYI Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Release (Apple M1)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Apple",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Mac arm64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI ASAN (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Intel",
+        short_name = "asn",
+    ),
+    triggered_by = ["GPU FYI Mac Builder (asan)"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Release (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Intel",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Retina ASAN (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|AMD|Retina",
+        short_name = "asn",
+    ),
+    triggered_by = ["GPU FYI Mac Builder (asan)"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Retina Debug (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|AMD|Retina",
+        short_name = "dbg",
+    ),
+    triggered_by = ["GPU FYI Mac Builder (dbg)"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Retina Debug (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Nvidia",
+        short_name = "dbg",
+    ),
+    triggered_by = ["GPU FYI Mac Builder (dbg)"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Retina Release (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|AMD|Retina",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac FYI Retina Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|Nvidia",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac Pro FYI Release (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac|AMD|Pro",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 Debug (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia",
+        short_name = "dbg",
+    ),
+    triggered_by = ["GPU FYI Win x64 Builder (dbg)"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 DX12 Vulkan Debug (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia|dx12vk",
+        short_name = "dbg",
+    ),
+    triggered_by = ["GPU FYI Win x64 DX12 Vulkan Builder (dbg)"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 DX12 Vulkan Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia|dx12vk",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win x64 DX12 Vulkan Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 Exp Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Intel",
+        short_name = "exp",
+    ),
+    triggered_by = ["GPU FYI Win x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 Exp Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia",
+        short_name = "exp",
+    ),
+    triggered_by = ["GPU FYI Win x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 Release (AMD RX 5500 XT)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|AMD",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 Release (Intel HD 630)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Intel",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win x64 Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x64 Release XR Perf (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia",
+        short_name = "xr",
+    ),
+    triggered_by = ["GPU FYI XR Win x64 Builder"],
+)
+
+# Builder + tester.
+gpu_fyi_windows_builder(
+    name = "Win10 FYI x64 SkiaRenderer Dawn Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x64|Nvidia",
+        short_name = "skd",
+    ),
+)
+
+ci.thin_tester(
+    name = "Win10 FYI x86 Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|10|x86|Nvidia",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win Builder"],
+)
+
+ci.thin_tester(
+    name = "Win7 FYI Release (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|7|x86|AMD",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win Builder"],
+)
+
+ci.thin_tester(
+    name = "Win7 FYI Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|7|x86|Nvidia",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win Builder"],
+)
+
+ci.thin_tester(
+    name = "Win7 FYI x64 Release (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|7|x64|Nvidia",
+        short_name = "rel",
+    ),
+    triggered_by = ["GPU FYI Win x64 Builder"],
+)
+
+gpu_fyi_windows_builder(
+    name = "GPU FYI Win Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|Release",
+        short_name = "x86",
+    ),
+)
+
+gpu_fyi_windows_builder(
+    name = "GPU FYI Win x64 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|Release",
+        short_name = "x64",
+    ),
+)
+
+gpu_fyi_windows_builder(
+    name = "GPU FYI Win x64 Builder (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|Debug",
+        short_name = "x64",
+    ),
+)
+
+gpu_fyi_windows_builder(
+    name = "GPU FYI Win x64 DX12 Vulkan Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|dx12vk",
+        short_name = "rel",
+    ),
+)
+
+gpu_fyi_windows_builder(
+    name = "GPU FYI Win x64 DX12 Vulkan Builder (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|dx12vk",
+        short_name = "dbg",
+    ),
+)
+
+gpu_fyi_windows_builder(
+    name = "GPU FYI XR Win x64 Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows|Builder|XR",
+        short_name = "x64",
+    ),
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.gpu.star b/infra/config/subprojects/chromium/ci/chromium.gpu.star
new file mode 100644
index 0000000..7863d35
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.gpu.star
@@ -0,0 +1,169 @@
+# 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.
+"""Definitions of builders in the chromium.gpu builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "sheriff_rotations")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.gpu",
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = ci.gpu.POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM_GPU,
+    tree_closing = True,
+    tree_closing_notifiers = ci.gpu.TREE_CLOSING_NOTIFIERS,
+    thin_tester_cores = 2,
+)
+
+consoles.console_view(
+    name = "chromium.gpu",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    ordering = {
+        None: ["Windows", "Mac", "Linux"],
+    },
+)
+
+ci.gpu.linux_builder(
+    name = "Android Release (Nexus 5X)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "Android",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.gpu.linux_builder(
+    name = "GPU Linux Builder",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+ci.gpu.linux_builder(
+    name = "GPU Linux Builder (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux",
+    ),
+    tree_closing = False,
+)
+
+ci.gpu.mac_builder(
+    name = "GPU Mac Builder",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+ci.gpu.mac_builder(
+    name = "GPU Mac Builder (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac",
+    ),
+    tree_closing = False,
+)
+
+ci.gpu.windows_builder(
+    name = "GPU Win x64 Builder",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+ci.gpu.windows_builder(
+    name = "GPU Win x64 Builder (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows",
+    ),
+    tree_closing = False,
+)
+
+ci.thin_tester(
+    name = "Linux Debug (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux",
+    ),
+    triggered_by = ["GPU Linux Builder (dbg)"],
+    tree_closing = False,
+)
+
+ci.thin_tester(
+    name = "Linux Release (NVIDIA)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    cq_mirrors_console_view = "mirrors",
+    console_view_entry = consoles.console_view_entry(
+        category = "Linux",
+    ),
+    triggered_by = ["ci/GPU Linux Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac Debug (Intel)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac",
+    ),
+    triggered_by = ["GPU Mac Builder (dbg)"],
+    tree_closing = False,
+)
+
+ci.thin_tester(
+    name = "Mac Release (Intel)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/GPU Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac Retina Debug (AMD)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac",
+    ),
+    triggered_by = ["GPU Mac Builder (dbg)"],
+    tree_closing = False,
+)
+
+ci.thin_tester(
+    name = "Mac Retina Release (AMD)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "Mac",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/GPU Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Win10 x64 Debug (NVIDIA)",
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows",
+    ),
+    triggered_by = ["GPU Win x64 Builder (dbg)"],
+    tree_closing = False,
+)
+
+ci.thin_tester(
+    name = "Win10 x64 Release (NVIDIA)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "Windows",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/GPU Win x64 Builder"],
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.linux.star b/infra/config/subprojects/chromium/ci/chromium.linux.star
new file mode 100644
index 0000000..3970811
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.linux.star
@@ -0,0 +1,397 @@
+# 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.
+"""Definitions of builders in the chromium.linux builder group."""
+
+load("//lib/args.star", "args")
+load("//lib/builders.star", "goma", "os", "sheriff_rotations")
+load("//lib/branches.star", "branches")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.linux",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
+    main_console_view = "main",
+    notifies = ["chromium.linux"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM,
+    tree_closing = True,
+)
+
+consoles.console_view(
+    name = "chromium.linux",
+    branch_selector = branches.STANDARD_MILESTONE,
+    ordering = {
+        None: ["release", "debug"],
+        "release": consoles.ordering(short_names = ["bld", "tst", "nsl", "gcc"]),
+        "cast": consoles.ordering(short_names = ["vid", "aud"]),
+    },
+)
+
+def linux_builder(
+        *,
+        name,
+        notifies = ("chromium.linux",),
+        extra_notifies = None,
+        **kwargs):
+    return ci.builder(
+        name = name,
+        notifies = list(notifies) + (extra_notifies or []),
+        **kwargs
+    )
+
+ci.builder(
+    name = "Cast Audio Linux",
+    console_view_entry = consoles.console_view_entry(
+        category = "cast",
+        short_name = "aud",
+    ),
+    ssd = True,
+)
+
+ci.builder(
+    name = "Cast Linux",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cast",
+        short_name = "vid",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_jobs = goma.jobs.J50,
+)
+
+ci.builder(
+    name = "Cast Linux Debug",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "cast",
+        short_name = "dbg",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    os = os.LINUX_BIONIC,
+    # TODO(crbug.com/1173333): Make it tree-closing.
+    tree_closing = False,
+)
+
+ci.builder(
+    name = "Cast Linux ARM64",
+    branch_selector = branches.MAIN,
+    console_view_entry = consoles.console_view_entry(
+        category = "cast",
+        short_name = "arm64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    os = os.LINUX_BIONIC,
+    tree_closing = False,
+)
+
+ci.builder(
+    name = "Deterministic Fuchsia (dbg)",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|x64",
+            short_name = "det",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "misc",
+            short_name = "det",
+        ),
+    ],
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+    goma_jobs = None,
+)
+
+ci.builder(
+    name = "Deterministic Linux",
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "det",
+    ),
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+    # Set tree_closing to false to disable the defaualt tree closer, which
+    # filters by step name, and instead enable tree closing for any step
+    # failure.
+    tree_closing = False,
+    notifies = ["Deterministic Linux", "close-on-any-step-failure"],
+)
+
+ci.builder(
+    name = "Deterministic Linux (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|builder",
+        short_name = "det",
+    ),
+    cores = 32,
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 7 * time.hour,
+)
+
+ci.builder(
+    name = "Fuchsia ARM64",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|a64",
+            short_name = "rel",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "ci",
+            short_name = "arm64",
+        ),
+    ],
+    cq_mirrors_console_view = "mirrors",
+    notifies = ["cr-fuchsia"],
+)
+
+ci.builder(
+    name = "Fuchsia x64",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|x64",
+            short_name = "rel",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "ci",
+            short_name = "x64",
+        ),
+    ],
+    cq_mirrors_console_view = "mirrors",
+    notifies = ["cr-fuchsia"],
+)
+
+ci.builder(
+    name = "Leak Detection Linux",
+    console_view_entry = consoles.console_view_entry(
+        console_view = "chromium.fyi",
+        category = "linux",
+        short_name = "lk",
+    ),
+    main_console_view = None,
+    notifies = args.ignore_default([]),
+    tree_closing = False,
+)
+
+ci.builder(
+    name = "Linux Builder",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "bld",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+ci.builder(
+    name = "Linux Builder (dbg)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|builder",
+        short_name = "64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Linux Builder (dbg)(32)",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|builder",
+        short_name = "32",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Linux Builder (Wayland)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "bld-wl",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Linux Tests",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "tst",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_backend = None,
+    triggered_by = ["ci/Linux Builder"],
+)
+
+ci.builder(
+    name = "Linux Tests (dbg)(1)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|tester",
+        short_name = "64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Linux Builder (dbg)"],
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Linux Tests (Wayland)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "tst-wl",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_backend = None,
+    triggered_by = ["ci/Linux Builder (Wayland)"],
+)
+
+ci.builder(
+    name = "fuchsia-arm64-cast",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|cast",
+            short_name = "a64",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "ci",
+            short_name = "arm64-cast",
+        ),
+    ],
+    cq_mirrors_console_view = "mirrors",
+    # Set tree_closing to false to disable the defaualt tree closer, which
+    # filters by step name, and instead enable tree closing for any step
+    # failure.
+    tree_closing = False,
+    notifies = ["cr-fuchsia", "close-on-any-step-failure"],
+)
+
+ci.builder(
+    name = "Network Service Linux",
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "nsl",
+    ),
+)
+
+ci.builder(
+    name = "fuchsia-x64-cast",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|cast",
+            short_name = "x64",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "ci",
+            short_name = "x64-cast",
+        ),
+    ],
+    cq_mirrors_console_view = "mirrors",
+    # Set tree_closing to false to disable the defaualt tree closer, which
+    # filters by step name, and instead enable tree closing for any step
+    # failure.
+    tree_closing = False,
+    notifies = ["cr-fuchsia", "close-on-any-step-failure"],
+)
+
+ci.builder(
+    name = "fuchsia-x64-dbg",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia|x64",
+            short_name = "dbg",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "ci",
+            short_name = "x64-dbg",
+        ),
+    ],
+    notifies = ["cr-fuchsia"],
+)
+
+ci.builder(
+    name = "linux-bfcache-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "bfcache",
+        short_name = "bfc",
+    ),
+)
+
+ci.builder(
+    name = "linux-extended-tracing-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "trc",
+    ),
+)
+
+ci.builder(
+    name = "linux-gcc-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "gcc",
+    ),
+    goma_backend = None,
+)
+
+ci.builder(
+    name = "linux-bionic-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "bio",
+    ),
+    os = os.LINUX_BIONIC,
+    tree_closing = False,
+)
+
+ci.builder(
+    name = "linux-trusty-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "tru",
+    ),
+    os = os.LINUX_TRUSTY,
+)
+
+ci.builder(
+    name = "linux-xenial-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "xen",
+    ),
+    os = os.LINUX_XENIAL,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.mac.star b/infra/config/subprojects/chromium/ci/chromium.mac.star
new file mode 100644
index 0000000..13b7c85
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.mac.star
@@ -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.
+"""Definitions of builders in the chromium.mac builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "cpu", "goma", "os", "sheriff_rotations", "xcode")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.mac",
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    main_console_view = "main",
+    os = os.MAC_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM,
+    thin_tester_cores = 8,
+    tree_closing = True,
+)
+
+consoles.console_view(
+    name = "chromium.mac",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    ordering = {
+        None: ["release"],
+        "release": consoles.ordering(short_names = ["bld"]),
+        "debug": consoles.ordering(short_names = ["bld"]),
+        "ios|default": consoles.ordering(short_names = ["dev", "sim"]),
+    },
+)
+
+consoles.console_view(
+    name = "sheriff.ios",
+    title = "iOS Sheriff Console",
+    ordering = {
+        "*type*": consoles.ordering(short_names = ["dev", "sim"]),
+        None: ["chromium.mac", "chromium.fyi"],
+        "chromium.mac": "*type*",
+        "chromium.fyi|13": "*type*",
+    },
+)
+
+def ios_builder(*, name, **kwargs):
+    kwargs.setdefault("sheriff_rotations", sheriff_rotations.IOS)
+    kwargs.setdefault("xcode", xcode.x13main)
+    return ci.builder(name = name, **kwargs)
+
+ci.builder(
+    name = "Mac Builder",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "bld",
+    ),
+    cq_mirrors_console_view = "mirrors",
+)
+
+ci.builder(
+    name = "Mac Builder (dbg)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug",
+        short_name = "bld",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    os = os.MAC_ANY,
+)
+
+ci.builder(
+    name = "mac-arm64-on-arm64-rel",
+
+    # TODO(crbug.com/1186823): Expand to more branches when all M1 bots are
+    # rosettaless.
+    branch_selector = branches.MAIN,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|arm64",
+        short_name = "a64",
+    ),
+    cpu = cpu.ARM64,
+    os = os.MAC_11,
+)
+
+ci.builder(
+    name = "mac-arm64-rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|arm64",
+        short_name = "bld",
+    ),
+    os = os.MAC_ANY,
+)
+
+ci.thin_tester(
+    name = "mac11-arm64-rel-tests",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|arm64",
+        short_name = "11",
+    ),
+    tree_closing = False,
+    triggered_by = ["ci/mac-arm64-rel"],
+)
+
+ci.thin_tester(
+    name = "Mac10.11 Tests",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "11",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac10.12 Tests",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "12",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac10.13 Tests",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "13",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac10.14 Tests",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "14",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac10.15 Tests",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release",
+        short_name = "15",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac11 Tests",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "11",
+    ),
+    triggered_by = ["ci/Mac Builder"],
+)
+
+ci.thin_tester(
+    name = "Mac11 Tests (dbg)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug",
+        short_name = "11",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Mac Builder (dbg)"],
+)
+
+ios_builder(
+    name = "ios-device",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "ios|default",
+            short_name = "dev",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.ios",
+            category = "chromium.mac",
+            short_name = "dev",
+        ),
+    ],
+    # We don't have necessary capacity to run this configuration in CQ, but it
+    # is part of the main waterfall
+)
+
+ios_builder(
+    name = "ios-simulator",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "ios|default",
+            short_name = "sim",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.ios",
+            category = "chromium.mac",
+            short_name = "sim",
+        ),
+    ],
+    cq_mirrors_console_view = "mirrors",
+)
+
+ios_builder(
+    name = "ios-simulator-full-configs",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "ios|default",
+            short_name = "ful",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.ios",
+            category = "chromium.mac",
+            short_name = "ful",
+        ),
+    ],
+    cq_mirrors_console_view = "mirrors",
+)
+
+ios_builder(
+    name = "ios-simulator-noncq",
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "ios|default",
+            short_name = "non",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.ios",
+            category = "chromium.mac",
+            short_name = "non",
+        ),
+    ],
+    # We don't have necessary capacity to run this configuration in CQ, but it
+    # is part of the main waterfall
+    xcode = xcode.x13main,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.memory.star b/infra/config/subprojects/chromium/ci/chromium.memory.star
new file mode 100644
index 0000000..168dd40
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.memory.star
@@ -0,0 +1,274 @@
+# 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.
+"""Definitions of builders in the chromium.memory builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os", "sheriff_rotations")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.memory",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    main_console_view = "main",
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM,
+    tree_closing = True,
+)
+
+consoles.console_view(
+    name = "chromium.memory",
+    branch_selector = branches.STANDARD_MILESTONE,
+    ordering = {
+        None: ["win", "mac", "linux", "cros"],
+        "*build-or-test*": consoles.ordering(short_names = ["bld", "tst"]),
+        "linux|TSan v2": "*build-or-test*",
+        "linux|asan lsan": "*build-or-test*",
+        "linux|webkit": consoles.ordering(short_names = ["asn", "msn"]),
+    },
+)
+
+def linux_memory_builder(*, name, **kwargs):
+    kwargs["notifies"] = kwargs.get("notifies", []) + ["linux-memory"]
+    return ci.builder(name = name, **kwargs)
+
+linux_memory_builder(
+    name = "Linux ASan LSan Builder",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|asan lsan",
+        short_name = "bld",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    os = os.LINUX_BIONIC,
+    ssd = True,
+)
+
+linux_memory_builder(
+    name = "Linux ASan LSan Tests (1)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|asan lsan",
+        short_name = "tst",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Linux ASan LSan Builder"],
+    os = os.LINUX_BIONIC,
+)
+
+linux_memory_builder(
+    name = "Linux ASan Tests (sandboxed)",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|asan lsan",
+        short_name = "sbx",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Linux ASan LSan Builder"],
+)
+
+linux_memory_builder(
+    name = "Linux TSan Builder",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|TSan v2",
+        short_name = "bld",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+linux_memory_builder(
+    name = "Linux CFI",
+    console_view_entry = consoles.console_view_entry(
+        category = "cfi",
+        short_name = "lnx",
+    ),
+    cores = 32,
+    # TODO(thakis): Remove once https://crbug.com/927738 is resolved.
+    execution_timeout = 5 * time.hour,
+    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
+)
+
+linux_memory_builder(
+    name = "Linux Chromium OS ASan LSan Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros|asan",
+        short_name = "bld",
+    ),
+    # TODO(crbug.com/1030593): Builds take more than 3 hours sometimes. Remove
+    # once the builds are faster.
+    execution_timeout = 6 * time.hour,
+)
+
+linux_memory_builder(
+    name = "Linux Chromium OS ASan LSan Tests (1)",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros|asan",
+        short_name = "tst",
+    ),
+    triggered_by = ["Linux Chromium OS ASan LSan Builder"],
+)
+
+linux_memory_builder(
+    name = "Linux ChromiumOS MSan Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros|msan",
+        short_name = "bld",
+    ),
+    execution_timeout = 4 * time.hour,
+)
+
+linux_memory_builder(
+    name = "Linux ChromiumOS MSan Tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "cros|msan",
+        short_name = "tst",
+    ),
+    execution_timeout = 4 * time.hour,
+    triggered_by = ["Linux ChromiumOS MSan Builder"],
+)
+
+linux_memory_builder(
+    name = "Linux MSan Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|msan",
+        short_name = "bld",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+linux_memory_builder(
+    name = "Linux MSan Tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|msan",
+        short_name = "tst",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.LOW_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+    triggered_by = ["Linux MSan Builder"],
+)
+
+ci.builder(
+    name = "Mac ASan 64 Builder",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "bld",
+    ),
+    goma_debug = True,  # TODO(hinoka): Remove this after debugging.
+    goma_jobs = None,
+    cores = None,  # Swapping between 8 and 24
+    os = os.MAC_DEFAULT,
+    triggering_policy = scheduler.greedy_batching(
+        max_concurrent_invocations = 2,
+    ),
+)
+
+linux_memory_builder(
+    name = "Linux TSan Tests",
+    branch_selector = branches.STANDARD_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|TSan v2",
+        short_name = "tst",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Linux TSan Builder"],
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.LOW_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Mac ASan 64 Tests (1)",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "tst",
+    ),
+    cores = 12,
+    os = os.MAC_DEFAULT,
+    triggered_by = ["Mac ASan 64 Builder"],
+)
+
+ci.builder(
+    name = "WebKit Linux ASAN",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|webkit",
+        short_name = "asn",
+    ),
+    os = os.LINUX_BIONIC_REMOVE,
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "WebKit Linux Leak",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|webkit",
+        short_name = "lk",
+    ),
+    os = os.LINUX_BIONIC_REMOVE,
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "WebKit Linux MSAN",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|webkit",
+        short_name = "msn",
+    ),
+    os = os.LINUX_BIONIC_REMOVE,
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.HIGH_JOBS_FOR_CI,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "android-asan",
+    console_view_entry = consoles.console_view_entry(
+        category = "android",
+        short_name = "asn",
+    ),
+    os = os.LINUX_DEFAULT,
+    sheriff_rotations = sheriff_rotations.ANDROID,
+    tree_closing = False,
+)
+
+ci.builder(
+    name = "linux-ubsan-vptr",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|ubsan",
+        short_name = "vpt",
+    ),
+    builderless = 1,
+    cores = 32,
+    tree_closing = False,
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "win-asan",
+    console_view_entry = consoles.console_view_entry(
+        category = "win",
+        short_name = "asn",
+    ),
+    cores = 32,
+    builderless = True,
+    os = os.WINDOWS_DEFAULT,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.mojo.star b/infra/config/subprojects/chromium/ci/chromium.mojo.star
new file mode 100644
index 0000000..7ed0b16b
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.mojo.star
@@ -0,0 +1,68 @@
+# 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.
+"""Definitions of builders in the chromium.updater builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/ci.star", "ci", "rbe_instance", "rbe_jobs")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.mojo",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = 10 * time.hour,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.console_view(
+    name = "chromium.mojo",
+)
+
+ci.builder(
+    name = "Mojo Android",
+    console_view_entry = consoles.console_view_entry(
+        short_name = "and",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Mojo ChromiumOS",
+    console_view_entry = consoles.console_view_entry(
+        short_name = "cr",
+    ),
+)
+
+ci.builder(
+    name = "Mojo Linux",
+    console_view_entry = consoles.console_view_entry(
+        short_name = "lnx",
+    ),
+    goma_backend = None,
+    reclient_jobs = rbe_jobs.DEFAULT,
+    reclient_instance = rbe_instance.DEFAULT,
+)
+
+ci.builder(
+    name = "Mojo Windows",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        short_name = "win",
+    ),
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "mac-mojo-rel",
+    console_view_entry = consoles.console_view_entry(
+        short_name = "mac",
+    ),
+    cores = 4,
+    os = os.MAC_ANY,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.packager.star b/infra/config/subprojects/chromium/ci/chromium.packager.star
new file mode 100644
index 0000000..5f68ee4f
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.packager.star
@@ -0,0 +1,260 @@
+# 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.
+"""Definitions of builders in the chromium.packager builder group."""
+
+load("//lib/builders.star", "os", "sheriff_rotations")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.packager",
+    cores = 8,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    os = os.LINUX_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = "chromium-cipd-builder@chops-service-accounts.iam.gserviceaccount.com",
+)
+
+consoles.console_view(
+    name = "chromium.packager",
+)
+
+ci.builder(
+    name = "3pp-linux-amd64-packager",
+    os = os.LINUX_DEFAULT,
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "3pp|linux",
+        short_name = "amd64",
+    ),
+    executable = "recipe:chromium_3pp",
+    notifies = ["chromium-3pp-packager"],
+    properties = {
+        "$build/chromium_3pp": {
+            "platform": "linux-amd64",
+            "preprocess": [{
+                "name": "third_party/android_deps",
+                "cmd": [
+                    "{CHECKOUT}/src/third_party/android_deps/fetch_all.py",
+                    "-v",
+                    "--ignore-vulnerabilities",
+                ],
+            }],
+            "gclient_config": "chromium",
+            "gclient_apply_config": ["android"],
+        },
+    },
+    schedule = "with 6h interval",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "3pp-mac-amd64-packager",
+    os = os.MAC_DEFAULT,
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "3pp|mac",
+        short_name = "amd64",
+    ),
+    cores = None,
+    executable = "recipe:chromium_3pp",
+    notifies = ["chromium-3pp-packager"],
+    properties = {
+        "$build/chromium_3pp": {
+            "platform": "mac-amd64",
+            "gclient_config": "chromium",
+        },
+    },
+    # TODO(crbug.com/1267449): Trigger builds routinely once works fine.
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "android-androidx-packager",
+    console_view_entry = consoles.console_view_entry(
+        category = "android",
+        short_name = "androidx",
+    ),
+    notifies = ["chromium-androidx-packager"],
+    executable = "recipe:android/androidx_packager",
+    schedule = "0 7,14,22 * * * *",
+    sheriff_rotations = sheriff_rotations.ANDROID,
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "android-avd-packager",
+    console_view_entry = consoles.console_view_entry(
+        category = "android",
+        short_name = "avd",
+    ),
+    executable = "recipe:android/avd_packager",
+    properties = {
+        "avd_configs": [
+            "tools/android/avd/proto/creation/generic_android23.textpb",
+            "tools/android/avd/proto/creation/generic_android27.textpb",
+            "tools/android/avd/proto/creation/generic_android28.textpb",
+            "tools/android/avd/proto/creation/generic_android29.textpb",
+            "tools/android/avd/proto/creation/generic_android30.textpb",
+            "tools/android/avd/proto/creation/generic_android31.textpb",
+            "tools/android/avd/proto/creation/generic_playstore_android27.textpb",
+            "tools/android/avd/proto/creation/generic_playstore_android28.textpb",
+            "tools/android/avd/proto/creation/generic_playstore_android30.textpb",
+            "tools/android/avd/proto/creation/generic_playstore_android31.textpb",
+        ],
+    },
+    os = os.LINUX_BIONIC_REMOVE,
+    # Triggered manually through the scheduler UI
+    # https://luci-scheduler.appspot.com/jobs/chromium/android-avd-packager
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "android-sdk-packager",
+    console_view_entry = consoles.console_view_entry(
+        category = "android",
+        short_name = "sdk",
+    ),
+    executable = "recipe:android/sdk_packager",
+    properties = {
+        # We still package part of build-tools;25.0.2 to support
+        # http://bit.ly/2KNUygZ
+        "packages": [
+            {
+                "sdk_package_name": "build-tools;25.0.2",
+                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/25.0.2.yaml",
+            },
+            {
+                "sdk_package_name": "build-tools;29.0.2",
+                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/29.0.2.yaml",
+            },
+            {
+                "sdk_package_name": "build-tools;30.0.1",
+                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/30.0.1.yaml",
+            },
+            {
+                "sdk_package_name": "build-tools;31.0.0",
+                "cipd_yaml": "third_party/android_sdk/cipd/build-tools/31.0.0.yaml",
+            },
+            {
+                "sdk_package_name": "cmdline-tools;latest",
+                "cipd_yaml": "third_party/android_sdk/cipd/cmdline-tools.yaml",
+            },
+            {
+                "sdk_package_name": "emulator",
+                "cipd_yaml": "third_party/android_sdk/cipd/emulator.yaml",
+            },
+            {
+                "sdk_package_name": "patcher;v4",
+                "cipd_yaml": "third_party/android_sdk/cipd/patcher/v4.yaml",
+            },
+            {
+                "sdk_package_name": "platforms;android-29",
+                "cipd_yaml": "third_party/android_sdk/cipd/platforms/android-29.yaml",
+            },
+            {
+                "sdk_package_name": "platforms;android-30",
+                "cipd_yaml": "third_party/android_sdk/cipd/platforms/android-30.yaml",
+            },
+            {
+                "sdk_package_name": "platforms;android-31",
+                "cipd_yaml": "third_party/android_sdk/cipd/platforms/android-31.yaml",
+            },
+            {
+                "sdk_package_name": "platform-tools",
+                "cipd_yaml": "third_party/android_sdk/cipd/platform-tools.yaml",
+            },
+            {
+                "sdk_package_name": "sources;android-29",
+                "cipd_yaml": "third_party/android_sdk/cipd/sources/android-29.yaml",
+            },
+            {
+                "sdk_package_name": "sources;android-30",
+                "cipd_yaml": "third_party/android_sdk/cipd/sources/android-30.yaml",
+            },
+            {
+                "sdk_package_name": "sources;android-31",
+                "cipd_yaml": "third_party/android_sdk/cipd/sources/android-31.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-23;google_apis;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-23/google_apis/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-27;google_apis;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-27/google_apis/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-27;google_apis_playstore;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-27/google_apis_playstore/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-28;google_apis;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-28/google_apis/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-28;google_apis_playstore;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-28/google_apis_playstore/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-29;google_apis;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-29/google_apis/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-29;google_apis_playstore;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-29/google_apis_playstore/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-30;google_apis;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-30/google_apis/x86.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-30;google_apis_playstore;x86",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-30/google_apis_playstore/x86.yaml",
+            },
+            # use x86_64 since sdkmanager don't ship x86 for android-31 and above.
+            {
+                "sdk_package_name": "system-images;android-31;google_apis;x86_64",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-31/google_apis/x86_64.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-31;google_apis_playstore;x86_64",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-31/google_apis_playstore/x86_64.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-32;google_apis;x86_64",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-32/google_apis/x86_64.yaml",
+            },
+            {
+                "sdk_package_name": "system-images;android-32;google_apis_playstore;x86_64",
+                "cipd_yaml": "third_party/android_sdk/cipd/system_images/android-32/google_apis_playstore/x86_64.yaml",
+            },
+        ],
+    },
+    schedule = "0 7 * * *",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "rts-model-packager",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "rts",
+        short_name = "create-model",
+    ),
+    cores = None,
+    executable = "recipe:chromium_rts/create_model",
+    execution_timeout = 8 * time.hour,
+    notifies = [
+        luci.notifier(
+            name = "rts-model-packager-notifier",
+            on_occurrence = ["FAILURE", "INFRA_FAILURE"],
+            notify_emails = ["chrome-browser-infra-team@google.com"],
+        ),
+    ],
+    schedule = "0 9 * * *",  # at 1AM or 2AM PT (depending on DST), once a day.
+    triggered_by = [],
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.rust.star b/infra/config/subprojects/chromium/ci/chromium.rust.star
new file mode 100644
index 0000000..2dfaf4b
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.rust.star
@@ -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.
+"""Definitions of builders in the chromium.rust builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.rust",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.console_view(
+    name = "chromium.rust",
+    ordering = {
+        None: [
+            "linux",
+            "android",
+        ],
+    },
+)
+
+ci.builder(
+    name = "android-rust-arm-rel",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "android|rel",
+        short_name = "32",
+    ),
+    notifies = ["chrome-memory-safety"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+ci.builder(
+    name = "linux-rust-x64-rel",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux|rel",
+        short_name = "64",
+    ),
+    notifies = ["chrome-memory-safety"],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.star b/infra/config/subprojects/chromium/ci/chromium.star
new file mode 100644
index 0000000..300d21a5
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.star
@@ -0,0 +1,485 @@
+# 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.
+"""Definitions of builders in the chromium builder group."""
+
+load("//lib/builders.star", "goma", "os", "sheriff_rotations")
+load("//lib/branches.star", "branches")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium",
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    main_console_view = "main",
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM,
+)
+
+consoles.console_view(
+    name = "chromium",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    include_experimental_builds = True,
+    ordering = {
+        "*type*": consoles.ordering(short_names = ["dbg", "rel", "off"]),
+        "android": "*type*",
+        "fuchsia": "*type*",
+        "linux": "*type*",
+        "mac": "*type*",
+        "win": "*type*",
+    },
+)
+
+ci.builder(
+    name = "android-archive-dbg",
+    # Bump to 32 if needed.
+    console_view_entry = consoles.console_view_entry(
+        category = "android",
+        short_name = "dbg",
+    ),
+    cores = 8,
+    execution_timeout = 4 * time.hour,
+    os = os.LINUX_BIONIC_REMOVE,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "android-archive-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "android",
+        short_name = "rel",
+    ),
+    cores = 32,
+    os = os.LINUX_BIONIC_REMOVE,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "android-archive-rel.json",
+            ],
+        },
+    },
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "android-official",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "android",
+        short_name = "off",
+    ),
+    cores = 32,
+    # See https://crbug.com/1153349#c22, as we update symbol_level=2, build
+    # needs longer time to complete.
+    execution_timeout = 7 * time.hour,
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "fuchsia-official",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = False,
+    console_view_entry = [
+        consoles.console_view_entry(
+            category = "fuchsia",
+            short_name = "off",
+        ),
+        consoles.console_view_entry(
+            branch_selector = branches.MAIN,
+            console_view = "sheriff.fuchsia",
+            category = "ci",
+            short_name = "off-x64",
+        ),
+    ],
+    cores = 32,
+    # TODO: Change this back down to something reasonable once these builders
+    # have populated their cached by getting through the compile step
+    execution_timeout = 10 * time.hour,
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "linux-archive-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+        short_name = "dbg",
+    ),
+    # Bump to 32 if needed.
+    cores = 8,
+    os = os.LINUX_BIONIC_REMOVE,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "linux-archive-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+        short_name = "rel",
+    ),
+    cores = 32,
+    notifies = ["linux-archive-rel"],
+    os = os.LINUX_BIONIC_REMOVE,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "linux-archive-rel.json",
+            ],
+        },
+    },
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "linux-archive-tagged",
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+        short_name = "tag",
+    ),
+    cores = 32,
+    execution_timeout = 7 * time.hour,
+    os = os.LINUX_BIONIC_REMOVE,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "archive_datas": [
+                {
+                    "files": [
+                        "chrome",
+                        "chrome-wrapper",
+                        "chrome_100_percent.pak",
+                        "chrome_200_percent.pak",
+                        "chrome_crashpad_handler",
+                        "chrome_sandbox",
+                        "icudtl.dat",
+                        "libEGL.so",
+                        "libGLESv2.so",
+                        "libvk_swiftshader.so",
+                        "libvulkan.so.1",
+                        "MEIPreload/manifest.json",
+                        "MEIPreload/preloaded_data.pb",
+                        "nacl_helper",
+                        "nacl_helper_bootstrap",
+                        "nacl_irt_x86_64.nexe",
+                        "product_logo_48.png",
+                        "resources.pak",
+                        "swiftshader/libGLESv2.so",
+                        "swiftshader/libEGL.so",
+                        "v8_context_snapshot.bin",
+                        "vk_swiftshader_icd.json",
+                        "xdg-mime",
+                        "xdg-settings",
+                    ],
+                    "dirs": ["ClearKeyCdm", "locales", "resources"],
+                    "gcs_bucket": "chromium-browser-versioned",
+                    "gcs_path": "experimental/Linux_x64_Tagged/{%chromium_version%}/chrome-linux.zip",
+                    "archive_type": "ARCHIVE_TYPE_ZIP",
+                },
+                {
+                    "files": [
+                        "chromedriver",
+                    ],
+                    "gcs_bucket": "chromium-browser-versioned",
+                    "gcs_path": "experimental/Linux_x64_Tagged/{%chromium_version%}/chromedriver_linux64.zip",
+                    "archive_type": "ARCHIVE_TYPE_ZIP",
+                },
+            ],
+        },
+    },
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "linux-official",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = False,
+    console_view_entry = consoles.console_view_entry(
+        category = "linux",
+        short_name = "off",
+    ),
+    cores = 32,
+    execution_timeout = 7 * time.hour,
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+ci.builder(
+    name = "mac-archive-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "dbg",
+    ),
+    # Bump to 8 cores if needed.
+    cores = 4,
+    os = os.MAC_DEFAULT,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "mac-archive-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "rel",
+    ),
+    cores = 12,
+    os = os.MAC_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "mac-archive-rel.json",
+            ],
+        },
+    },
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "mac-archive-tagged",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac",
+        short_name = "tag",
+    ),
+    cores = 12,
+    execution_timeout = 7 * time.hour,
+    os = os.MAC_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "mac-tagged.json",
+            ],
+        },
+    },
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "mac-arm64-archive-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac|arm",
+        short_name = "dbg",
+    ),
+    cores = 12,
+    os = os.MAC_DEFAULT,
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "mac-arm64-archive-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac|arm",
+        short_name = "rel",
+    ),
+    cores = 12,
+    os = os.MAC_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "mac-arm64-archive-rel.json",
+            ],
+        },
+    },
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "mac-arm64-archive-tagged",
+    console_view_entry = consoles.console_view_entry(
+        category = "mac|arm",
+        short_name = "tag",
+    ),
+    cores = 12,
+    execution_timeout = 7 * time.hour,
+    os = os.MAC_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "mac-tagged.json",
+            ],
+        },
+    },
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "mac-official",
+    builderless = False,
+    # TODO(crbug.com/1072012) Use the default console view and use the default
+    # main console view once the build is green
+    main_console_view = None,
+    console_view_entry = consoles.console_view_entry(
+        console_view = "chromium.fyi",
+        category = "mac",
+        short_name = "off",
+    ),
+    # TODO(crbug.com/1279290) builds with PGO change take long time.
+    execution_timeout = 30 * time.hour,
+    os = os.MAC_ANY,
+)
+
+ci.builder(
+    name = "win-archive-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "win|dbg",
+        short_name = "64",
+    ),
+    cores = 32,
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win-archive-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "win|rel",
+        short_name = "64",
+    ),
+    cores = 32,
+    os = os.WINDOWS_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "win-archive-rel.json",
+            ],
+        },
+    },
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "win-archive-tagged",
+    console_view_entry = consoles.console_view_entry(
+        category = "win|tag",
+        short_name = "64",
+    ),
+    cores = 32,
+    execution_timeout = 7 * time.hour,
+    os = os.WINDOWS_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "win-tagged.json",
+            ],
+        },
+    },
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "win-official",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "win|off",
+        short_name = "64",
+    ),
+    cores = 32,
+    # TODO(crbug.com/1155416) builds with PGO change take long time.
+    execution_timeout = 7 * time.hour,
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win32-archive-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "win|dbg",
+        short_name = "32",
+    ),
+    cores = 32,
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win32-archive-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "win|rel",
+        short_name = "32",
+    ),
+    cores = 32,
+    os = os.WINDOWS_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "win32-archive-rel.json",
+            ],
+        },
+    },
+    tree_closing = True,
+)
+
+ci.builder(
+    name = "win32-archive-tagged",
+    console_view_entry = consoles.console_view_entry(
+        category = "win|tag",
+        short_name = "32",
+    ),
+    cores = 32,
+    execution_timeout = 7 * time.hour,
+    os = os.WINDOWS_DEFAULT,
+    properties = {
+        # The format of these properties is defined at archive/properties.proto
+        "$build/archive": {
+            "source_side_spec_path": [
+                "src",
+                "infra",
+                "archive_config",
+                "win-tagged.json",
+            ],
+        },
+    },
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "win32-official",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "win|off",
+        short_name = "32",
+    ),
+    cores = 32,
+    # TODO(crbug.com/1155416) builds with PGO change take long time.
+    execution_timeout = 7 * time.hour,
+    os = os.WINDOWS_DEFAULT,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.swangle.star b/infra/config/subprojects/chromium/ci/chromium.swangle.star
new file mode 100644
index 0000000..0fdcbe3
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.swangle.star
@@ -0,0 +1,139 @@
+# 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.
+"""Definitions of builders in the chromium.swangle builder group."""
+
+load("//lib/builders.star", "goma", "sheriff_rotations")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.swangle",
+    executable = "recipe:angle_chromium",
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = ci.gpu.POOL,
+    service_account = ci.gpu.SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM_GPU,
+)
+
+consoles.console_view(
+    name = "chromium.swangle",
+    ordering = {
+        None: ["DEPS", "ToT ANGLE", "ToT SwiftShader"],
+        "*os*": ["Windows", "Mac"],
+        "*cpu*": consoles.ordering(short_names = ["x86", "x64"]),
+        "DEPS": "*os*",
+        "DEPS|Windows": "*cpu*",
+        "DEPS|Linux": "*cpu*",
+        "ToT ANGLE": "*os*",
+        "ToT ANGLE|Windows": "*cpu*",
+        "ToT ANGLE|Linux": "*cpu*",
+        "ToT SwiftShader": "*os*",
+        "ToT SwiftShader|Windows": "*cpu*",
+        "ToT SwiftShader|Linux": "*cpu*",
+        "Chromium": "*os*",
+    },
+)
+
+ci.gpu.linux_builder(
+    name = "linux-swangle-chromium-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "Chromium|Linux",
+        short_name = "x64",
+    ),
+    executable = ci.DEFAULT_EXECUTABLE,
+)
+
+ci.gpu.linux_builder(
+    name = "linux-swangle-tot-angle-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT ANGLE|Linux",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "linux-swangle-tot-swiftshader-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT SwiftShader|Linux",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.linux_builder(
+    name = "linux-swangle-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Linux",
+        short_name = "x64",
+    ),
+    executable = ci.DEFAULT_EXECUTABLE,
+)
+
+ci.gpu.mac_builder(
+    name = "mac-swangle-chromium-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "Chromium|Mac",
+        short_name = "x64",
+    ),
+    executable = ci.DEFAULT_EXECUTABLE,
+)
+
+ci.gpu.windows_builder(
+    name = "win-swangle-chromium-x86",
+    console_view_entry = consoles.console_view_entry(
+        category = "Chromium|Windows",
+        short_name = "x86",
+    ),
+    executable = ci.DEFAULT_EXECUTABLE,
+)
+
+ci.gpu.windows_builder(
+    name = "win-swangle-tot-angle-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT ANGLE|Windows",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "win-swangle-tot-angle-x86",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT ANGLE|Windows",
+        short_name = "x86",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "win-swangle-tot-swiftshader-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT SwiftShader|Windows",
+        short_name = "x64",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "win-swangle-tot-swiftshader-x86",
+    console_view_entry = consoles.console_view_entry(
+        category = "ToT SwiftShader|Windows",
+        short_name = "x86",
+    ),
+)
+
+ci.gpu.windows_builder(
+    name = "win-swangle-x64",
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows",
+        short_name = "x64",
+    ),
+    executable = ci.DEFAULT_EXECUTABLE,
+)
+
+ci.gpu.windows_builder(
+    name = "win-swangle-x86",
+    console_view_entry = consoles.console_view_entry(
+        category = "DEPS|Windows",
+        short_name = "x86",
+    ),
+    executable = ci.DEFAULT_EXECUTABLE,
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.updater.star b/infra/config/subprojects/chromium/ci/chromium.updater.star
new file mode 100644
index 0000000..e0da8a4
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.updater.star
@@ -0,0 +1,290 @@
+# 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.
+"""Definitions of builders in the chromium.updater builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.updater",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.console_view(
+    name = "chromium.updater",
+)
+
+# The chromium.updater console includes some entries from official chrome builders.
+[branches.console_view_entry(
+    builder = "chrome:official/{}".format(name),
+    console_view = "chromium.updater",
+    category = category,
+    short_name = short_name,
+) for name, category, short_name in (
+    ("mac64", "official|mac", "64"),
+    ("mac-arm64", "official|mac", "arm64"),
+    ("win-asan", "official|win", "asan"),
+    ("win-clang", "official|win", "clang"),
+    ("win64-clang", "official|win", "clang (64)"),
+)]
+
+ci.builder(
+    name = "mac-updater-builder-dbg",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "bld",
+    ),
+    cores = None,
+    os = os.MAC_ANY,
+)
+
+ci.builder(
+    name = "mac-updater-builder-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "bld",
+    ),
+    cores = None,
+    os = os.MAC_ANY,
+)
+
+# TODO(gbeaty) Switch tester to use ci.thin_tester
+ci.builder(
+    name = "mac10.11-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "10.11",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "mac10.11-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "10.11",
+    ),
+    triggered_by = ["mac-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "mac10.12-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "10.12",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "mac10.12-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "10.12",
+    ),
+    triggered_by = ["mac-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "mac10.13-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "10.13",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "mac10.13-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "10.13",
+    ),
+    triggered_by = ["mac-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "mac10.14-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "10.14",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "mac10.14-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "10.14",
+    ),
+    triggered_by = ["mac-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "mac10.15-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "10.15",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "mac10.15-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "10.15",
+    ),
+    triggered_by = ["mac-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "mac11.0-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "11.0",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "mac11.0-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "11.0",
+    ),
+    triggered_by = ["mac-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "mac-arm64-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|mac",
+        short_name = "11.0 arm64",
+    ),
+    triggered_by = ["mac-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "mac-arm64-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|mac",
+        short_name = "11.0 arm64",
+    ),
+    triggered_by = ["mac-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "win-updater-builder-dbg",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|win (64)",
+        short_name = "bld",
+    ),
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win32-updater-builder-dbg",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|win (32)",
+        short_name = "bld",
+    ),
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win-updater-builder-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|win (64)",
+        short_name = "bld",
+    ),
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win32-updater-builder-rel",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|win (32)",
+        short_name = "bld",
+    ),
+    os = os.WINDOWS_DEFAULT,
+)
+
+ci.builder(
+    name = "win7-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|win (64)",
+        short_name = "7",
+    ),
+    triggered_by = ["win-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "win7(32)-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|win (32)",
+        short_name = "7",
+    ),
+    triggered_by = ["win32-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "win7-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|win (64)",
+        short_name = "7",
+    ),
+    triggered_by = ["win-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "win7(32)-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|win (32)",
+        short_name = "7",
+    ),
+    triggered_by = ["win32-updater-builder-rel"],
+)
+
+ci.builder(
+    name = "win10-updater-tester-dbg",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|win (64)",
+        short_name = "10",
+    ),
+    triggered_by = ["win-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "win10-updater-tester-dbg-uac",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|win (64)",
+        short_name = "UAC",
+    ),
+    triggered_by = ["win-updater-builder-dbg"],
+)
+
+ci.builder(
+    name = "win10-updater-tester-rel",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|win (64)",
+        short_name = "10",
+    ),
+    triggered_by = ["win-updater-builder-rel"],
+)
diff --git a/infra/config/subprojects/chromium/ci/chromium.win.star b/infra/config/subprojects/chromium/ci/chromium.win.star
new file mode 100644
index 0000000..b498fe0
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/chromium.win.star
@@ -0,0 +1,166 @@
+# 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.
+"""Definitions of builders in the chromium.win builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os", "sheriff_rotations")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "chromium.win",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    main_console_view = "main",
+    os = os.WINDOWS_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+    sheriff_rotations = sheriff_rotations.CHROMIUM,
+    tree_closing = True,
+)
+
+consoles.console_view(
+    name = "chromium.win",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    ordering = {
+        None: ["release", "debug"],
+        "debug|builder": consoles.ordering(short_names = ["64", "32"]),
+        "debug|tester": consoles.ordering(short_names = ["7", "10"]),
+    },
+)
+
+ci.builder(
+    name = "WebKit Win10",
+    console_view_entry = consoles.console_view_entry(
+        category = "misc",
+        short_name = "wbk",
+    ),
+    triggered_by = ["Win Builder"],
+)
+
+ci.builder(
+    name = "Win Builder",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|builder",
+        short_name = "32",
+    ),
+    cores = 32,
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "Win x64 Builder (dbg)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|builder",
+        short_name = "64",
+    ),
+    cores = 32,
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "Win10 Tests x64 (dbg)",
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|tester",
+        short_name = "10",
+    ),
+    triggered_by = ["Win x64 Builder (dbg)"],
+    # Too flaky. See crbug.com/876224 for more details.
+    tree_closing = False,
+)
+
+ci.thin_tester(
+    name = "Win7 (32) Tests",
+    console_view_entry = consoles.console_view_entry(
+        category = "release|tester",
+        short_name = "32",
+    ),
+    triggered_by = ["Win Builder"],
+)
+
+ci.builder(
+    name = "Win7 Tests (1)",
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|tester",
+        short_name = "32",
+    ),
+    os = os.WINDOWS_10,
+    triggered_by = ["Win Builder"],
+)
+
+ci.builder(
+    name = "Win7 Tests (dbg)(1)",
+    builderless = True,
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|tester",
+        short_name = "7",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    os = os.WINDOWS_10,
+    triggered_by = ["ci/Win Builder (dbg)"],
+)
+
+ci.builder(
+    name = "Win 7 Tests x64 (1)",
+    builderless = True,
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|tester",
+        short_name = "64",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    os = os.WINDOWS_10,
+    triggered_by = ["ci/Win x64 Builder"],
+)
+
+ci.builder(
+    name = "Win Builder (dbg)",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "debug|builder",
+        short_name = "32",
+    ),
+    cores = 32,
+    cq_mirrors_console_view = "mirrors",
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "Win x64 Builder",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|builder",
+        short_name = "64",
+    ),
+    cores = 32,
+    cq_mirrors_console_view = "mirrors",
+    os = os.WINDOWS_ANY,
+)
+
+ci.builder(
+    name = "Win10 Tests x64",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    console_view_entry = consoles.console_view_entry(
+        category = "release|tester",
+        short_name = "w10",
+    ),
+    cq_mirrors_console_view = "mirrors",
+    triggered_by = ["ci/Win x64 Builder"],
+)
+
+ci.builder(
+    name = "Windows deterministic",
+    console_view_entry = consoles.console_view_entry(
+        category = "misc",
+        short_name = "det",
+    ),
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 12 * time.hour,
+    goma_jobs = goma.jobs.J150,
+)
diff --git a/infra/config/subprojects/chromium/ci/infra.star b/infra/config/subprojects/chromium/ci/infra.star
new file mode 100644
index 0000000..d3a9bd6
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/infra.star
@@ -0,0 +1,91 @@
+# 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.
+"""Definitions of builders in the infra builder group."""
+
+load("//lib/builder_config.star", "builder_config")
+load("//lib/builders.star", "goma", "os")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    builder_group = "infra",
+    cores = 8,
+    executable = ci.DEFAULT_EXECUTABLE,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+    service_account = ci.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.console_view(
+    name = "infra",
+)
+
+ci.builder(
+    name = "linux-bootstrap",
+    bootstrap = True,
+    builder_spec = builder_config.builder_spec(
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "bootstrap|linux",
+        short_name = "bld",
+    ),
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "linux-bootstrap-tests",
+    bootstrap = True,
+    builder_spec = builder_config.builder_spec(
+        execution_mode = builder_config.execution_mode.TEST,
+        parent = "ci/linux-bootstrap",
+        chromium_config = builder_config.chromium_config(
+            config = "chromium",
+            apply_configs = ["mb"],
+            build_config = builder_config.build_config.RELEASE,
+            target_bits = 64,
+        ),
+        gclient_config = builder_config.gclient_config(
+            config = "chromium",
+        ),
+    ),
+    console_view_entry = consoles.console_view_entry(
+        category = "bootstrap|linux",
+        short_name = "tst",
+    ),
+)
+
+ci.builder(
+    name = "win-bootstrap",
+    bootstrap = True,
+    builderless = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "bootstrap|win",
+        short_name = "bld",
+    ),
+    os = os.WINDOWS_10,
+    schedule = "triggered",
+    triggered_by = [],
+)
+
+ci.builder(
+    name = "win-bootstrap-tests",
+    bootstrap = True,
+    console_view_entry = consoles.console_view_entry(
+        category = "bootstrap|win",
+        short_name = "tst",
+    ),
+    triggered_by = ["ci/win-bootstrap"],
+)
diff --git a/infra/config/subprojects/chromium/ci/metadata.exporter.star b/infra/config/subprojects/chromium/ci/metadata.exporter.star
new file mode 100644
index 0000000..1914d194
--- /dev/null
+++ b/infra/config/subprojects/chromium/ci/metadata.exporter.star
@@ -0,0 +1,38 @@
+# 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.
+"""Definitions of builders in the metadata.exporter builder group."""
+
+load("//lib/builders.star", "goma", "os", "sheriff_rotations")
+load("//lib/ci.star", "ci")
+load("//lib/consoles.star", "consoles")
+
+ci.defaults.set(
+    cores = 8,
+    execution_timeout = ci.DEFAULT_EXECUTION_TIMEOUT,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = ci.DEFAULT_POOL,
+)
+
+consoles.console_view(
+    name = "metadata.exporter",
+    header = None,
+)
+
+ci.builder(
+    name = "metadata-exporter",
+    # TODO(gbeaty) Remove this after prod freeze, the recipe doesn't require a
+    # builder group
+    builder_group = "chromium.linux",
+    console_view_entry = consoles.console_view_entry(
+        console_view = "metadata.exporter",
+    ),
+    executable = "recipe:chromium_export_metadata",
+    # TODO(gbeaty) Remove the goma values after prod freeze, the recipe doesn't
+    # do builds
+    goma_backend = goma.backend.RBE_PROD,
+    goma_jobs = goma.jobs.MANY_JOBS_FOR_CI,
+    notifies = "metadata-mapping",
+    service_account = "component-mapping-updater@chops-service-accounts.iam.gserviceaccount.com",
+    sheriff_rotations = sheriff_rotations.CHROMIUM,
+)
diff --git a/infra/config/subprojects/chromium/subproject.star b/infra/config/subprojects/chromium/subproject.star
index 3c35dce..bcaecf61 100644
--- a/infra/config/subprojects/chromium/subproject.star
+++ b/infra/config/subprojects/chromium/subproject.star
@@ -2,34 +2,22 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-load("//lib/branches.star", "branches")
+load("//lib/consoles.star", "consoles")
+load("//console-header.star", "HEADER")
+load("//project.star", "settings")
+
+consoles.defaults.set(
+    header = HEADER,
+    repo = "https://chromium.googlesource.com/chromium/src",
+    refs = [settings.ref],
+)
 
 exec("./ci.star")
 exec("./try.star")
+
+# TODO(gbeaty) Move the builders in these files into the per-builder group
+# files, this can't be done during the freeze because it changes the grace
+# period
 exec("./gpu.try.star")
 exec("./angle.try.star")
 exec("./swangle.try.star")
-
-# TODO(gbeaty) external_console_view uses new fields/types that aren't present
-# in the version of the protobuf that lint-luci-milo uses, so update protos and
-# then uncomment these (the main console is still reachable via the beta link in
-# the header)
-# luci.external_console_view(
-#     name = "main-m86",
-#     title = "Chromium M86 Main Console",
-#     source = "chromium-m86:main",
-# )
-
-# luci.external_console_view(
-#     name = "mirrors-m86",
-#     title = "Chromium M86 CQ Mirrors Console",
-#     source = "chromium-m86:mirrors",
-# )
-
-# luci.external_console_view(
-#     name = "try-m86",
-#     title = "Chromium M86 CQ Console",
-#     source = "chromium-m86:try",
-# )
-
-branches.exec("./fallback-cq.star")
diff --git a/infra/config/subprojects/chromium/swangle.try.star b/infra/config/subprojects/chromium/swangle.try.star
index 03d363f0..088653d6 100644
--- a/infra/config/subprojects/chromium/swangle.try.star
+++ b/infra/config/subprojects/chromium/swangle.try.star
@@ -2,26 +2,29 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-load("//lib/builders.star", "cpu", "os")
+load("//lib/builders.star", "cpu", "goma", "os")
+load("//lib/consoles.star", "consoles")
 load("//lib/try.star", "try_")
 
 try_.defaults.set(
     bucket = "try",
     build_numbers = True,
+    builderless = True,
+    builder_group = "tryserver.chromium.swangle",
     caches = [
         swarming.cache(
             name = "win_toolchain",
             path = "win_toolchain",
         ),
     ],
-    cores = 8,
     cpu = cpu.X86_64,
     cq_group = "cq",
-    executable = "recipe:chromium_trybot",
+    executable = "recipe:angle_chromium_trybot",
     execution_timeout = 2 * time.hour,
     # Max. pending time for builds. CQ considers builds pending >2h as timed
     # out: http://shortn/_8PaHsdYmlq. Keep this in sync.
     expiration_timeout = 2 * time.hour,
+    goma_backend = goma.backend.RBE_PROD,
     os = os.LINUX_DEFAULT,
     pool = "luci.chromium.try",
     service_account = "chromium-try-gpu-builder@chops-service-accounts.iam.gserviceaccount.com",
@@ -29,71 +32,89 @@
     task_template_canary_percentage = 5,
 )
 
-try_.chromium_swangle_linux_builder(
-    name = "linux-swangle-chromium-try-x64",
-    pool = "luci.chromium.swangle.chromium.linux.x64.try",
-    execution_timeout = 6 * time.hour,
-    pinned = False,
+consoles.list_view(
+    name = "tryserver.chromium.swangle",
 )
 
-try_.chromium_swangle_linux_builder(
+def swangle_linux_builder(*, name, **kwargs):
+    kwargs.setdefault("cores", 8)
+    kwargs.setdefault("os", os.LINUX_BIONIC_SWITCH_TO_DEFAULT)
+    return try_.builder(name = name, **kwargs)
+
+def swangle_mac_builder(*, name, **kwargs):
+    kwargs.setdefault("os", os.MAC_ANY)
+    return try_.builder(name = name, **kwargs)
+
+def swangle_windows_builder(*, name, **kwargs):
+    kwargs.setdefault("cores", 8)
+    kwargs.setdefault("os", os.WINDOWS_DEFAULT)
+    return try_.builder(name = name, **kwargs)
+
+swangle_linux_builder(
+    name = "linux-swangle-chromium-try-x64",
+    pool = "luci.chromium.swangle.chromium.linux.x64.try",
+    executable = "recipe:chromium_trybot",
+    execution_timeout = 6 * time.hour,
+)
+
+swangle_linux_builder(
     name = "linux-swangle-try-tot-angle-x64",
     pool = "luci.chromium.swangle.angle.linux.x64.try",
 )
 
-try_.chromium_swangle_linux_builder(
+swangle_linux_builder(
     name = "linux-swangle-try-tot-swiftshader-x64",
     pool = "luci.chromium.swangle.sws.linux.x64.try",
 )
 
-try_.chromium_swangle_linux_builder(
+swangle_linux_builder(
     name = "linux-swangle-try-x64",
     pool = "luci.chromium.swangle.deps.linux.x64.try",
-    pinned = False,
+    executable = "recipe:chromium_trybot",
 )
 
-try_.chromium_swangle_mac_builder(
+swangle_mac_builder(
     name = "mac-swangle-chromium-try-x64",
     pool = "luci.chromium.swangle.chromium.mac.x64.try",
+    executable = "recipe:chromium_trybot",
     execution_timeout = 6 * time.hour,
-    pinned = False,
 )
 
-try_.chromium_swangle_windows_builder(
+swangle_windows_builder(
     name = "win-swangle-chromium-try-x86",
     pool = "luci.chromium.swangle.chromium.win.x86.try",
+    executable = "recipe:chromium_trybot",
     execution_timeout = 6 * time.hour,
-    pinned = False,
 )
 
-try_.chromium_swangle_windows_builder(
+swangle_windows_builder(
     name = "win-swangle-try-tot-angle-x64",
     pool = "luci.chromium.swangle.win.x64.try",
 )
 
-try_.chromium_swangle_windows_builder(
+swangle_windows_builder(
     name = "win-swangle-try-tot-angle-x86",
     pool = "luci.chromium.swangle.angle.win.x86.try",
 )
 
-try_.chromium_swangle_windows_builder(
+swangle_windows_builder(
     name = "win-swangle-try-tot-swiftshader-x64",
     pool = "luci.chromium.swangle.win.x64.try",
 )
 
-try_.chromium_swangle_windows_builder(
+swangle_windows_builder(
     name = "win-swangle-try-tot-swiftshader-x86",
     pool = "luci.chromium.swangle.sws.win.x86.try",
 )
 
-try_.chromium_swangle_windows_builder(
+swangle_windows_builder(
     name = "win-swangle-try-x64",
     pool = "luci.chromium.swangle.win.x64.try",
-    pinned = False,
+    executable = "recipe:chromium_trybot",
 )
 
-try_.chromium_swangle_windows_builder(
+swangle_windows_builder(
     name = "win-swangle-try-x86",
     pool = "luci.chromium.swangle.deps.win.x86.try",
-    pinned = False,
+    executable = "recipe:chromium_trybot",
 )
diff --git a/infra/config/subprojects/chromium/try.star b/infra/config/subprojects/chromium/try.star
index 3bdbfca..796b4e2 100644
--- a/infra/config/subprojects/chromium/try.star
+++ b/infra/config/subprojects/chromium/try.star
@@ -3,10 +3,10 @@
 # found in the LICENSE file.
 
 load("//lib/branches.star", "branches")
-load("//lib/builders.star", "cpu", "goma", "os", "xcode")
+load("//lib/builders.star", "cpu")
 load("//lib/consoles.star", "consoles")
 load("//lib/try.star", "try_")
-load("//project.star", "branch_type", "settings")
+load("//project.star", "settings")
 
 try_.defaults.set(
     bucket = "try",
@@ -17,18 +17,12 @@
             path = "win_toolchain",
         ),
     ],
-    cores = 8,
     cpu = cpu.X86_64,
     cq_group = "cq",
-    executable = "recipe:chromium_trybot",
-    execution_timeout = 4 * time.hour,
     # Max. pending time for builds. CQ considers builds pending >2h as timed
     # out: http://shortn/_8PaHsdYmlq. Keep this in sync.
     expiration_timeout = 2 * time.hour,
     grace_period = 2 * time.minute,
-    os = os.LINUX_DEFAULT,
-    pool = "luci.chromium.try",
-    service_account = "chromium-try-builder@chops-service-accounts.iam.gserviceaccount.com",
     subproject_list_view = "luci.chromium.try",
     task_template_canary_percentage = 5,
 )
@@ -93,13 +87,7 @@
     ],
 )
 
-# Automatically maintained consoles
-
-consoles.list_view(
-    name = "presubmit",
-    branch_selector = branches.ALL_BRANCHES,
-    title = "presubmit builders",
-)
+branches.exec("./fallback-cq.star")
 
 consoles.list_view(
     name = "try",
@@ -112,2230 +100,20 @@
     branch_selector = branches.ALL_BRANCHES,
 )
 
-consoles.list_view(
-    name = "tryserver.blink",
-    branch_selector = branches.STANDARD_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.chromium",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.android",
-    branch_selector = branches.STANDARD_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.angle",
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.chromiumos",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.dawn",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.linux",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.mac",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.packager",
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.rust",
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.swangle",
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.updater",
-)
-
-consoles.list_view(
-    name = "tryserver.chromium.win",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-)
-
-consoles.list_view(
-    name = "tryserver.infra",
-)
-
-# Builders are sorted first lexicographically by the function used to define
-# them, then lexicographically by their name
-
-try_.blink_builder(
-    name = "linux-blink-optional-highdpi-rel",
-    goma_backend = goma.backend.RBE_PROD,
-)
-
-try_.blink_builder(
-    name = "linux-blink-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    goma_backend = goma.backend.RBE_PROD,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/cc/.+",
-            ".+/[+]/third_party/blink/renderer/core/paint/.+",
-            ".+/[+]/third_party/blink/renderer/core/svg/.+",
-            ".+/[+]/third_party/blink/renderer/platform/graphics/.+",
-        ],
-    ),
-)
-
-try_.blink_builder(
-    name = "win10.20h2-blink-rel",
-    goma_backend = goma.backend.RBE_PROD,
-    os = os.WINDOWS_ANY,
-    builderless = True,
-)
-
-try_.blink_builder(
-    name = "win7-blink-rel",
-    goma_backend = goma.backend.RBE_PROD,
-    os = os.WINDOWS_ANY,
-    builderless = True,
-)
-
-try_.blink_mac_builder(
-    name = "mac10.12-blink-rel",
-)
-
-try_.blink_mac_builder(
-    name = "mac10.13-blink-rel",
-)
-
-try_.blink_mac_builder(
-    name = "mac10.14-blink-rel",
-)
-
-try_.blink_mac_builder(
-    name = "mac10.15-blink-rel",
-)
-
-try_.blink_mac_builder(
-    name = "mac11.0-blink-rel",
-    builderless = False,
-)
-
-try_.blink_mac_builder(
-    name = "mac11.0.arm64-blink-rel",
-)
-
-try_.chromium_builder(
-    name = "android-official",
-    branch_selector = branches.STANDARD_MILESTONE,
-    cores = 32,
-)
-
-try_.chromium_builder(
-    name = "fuchsia-official",
-    branch_selector = branches.STANDARD_MILESTONE,
-    cores = 32,
-)
-
-try_.chromium_builder(
-    name = "linux-official",
-    branch_selector = branches.STANDARD_MILESTONE,
-    cores = 32,
-)
-
-try_.chromium_builder(
-    name = "mac-official",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    cores = None,  # TODO(thakis): Bump this up.
-    os = os.MAC_ANY,
-    # TODO(crbug.com/1279290):
-    # builds with LTO change take long time.
-    # Keep in sync with mac-official in main.star.
-    execution_timeout = 30 * time.hour,
-)
-
-try_.chromium_builder(
-    name = "win-official",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    os = os.WINDOWS_DEFAULT,
-    cores = 32,
-    execution_timeout = 6 * time.hour,
-)
-
-try_.chromium_builder(
-    name = "win32-official",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    os = os.WINDOWS_DEFAULT,
-    cores = 32,
-    execution_timeout = 6 * time.hour,
-)
-
-try_.chromium_android_builder(
-    name = "android-10-arm64-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-11-x86-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-12-x64-fyi-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-asan",
-)
-
-try_.chromium_android_builder(
-    name = "android-bfcache-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-binary-size",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    # TODO (kimstephanie): Change to cores = 16 and ssd = True once bots have
-    # landed
-    cores = 16,
-    executable = "recipe:binary_size_trybot",
-    goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
-    properties = {
-        "$build/binary_size": {
-            "analyze_targets": [
-                "//chrome/android:monochrome_public_minimal_apks",
-                "//chrome/android:trichrome_minimal_apks",
-                "//chrome/android:validate_expectations",
-                "//tools/binary_size:binary_size_trybot_py",
-            ],
-            "compile_targets": [
-                "monochrome_public_minimal_apks",
-                "monochrome_static_initializers",
-                "trichrome_minimal_apks",
-                "validate_expectations",
-            ],
-        },
-    },
-    tryjob = try_.job(),
-    # TODO(crbug/1202741)
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-    ssd = True,
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-arm-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/components/cronet/.+",
-            ".+/[+]/components/grpc_support/.+",
-            ".+/[+]/build/android/.+",
-            ".+/[+]/build/config/android/.+",
-        ],
-        location_regexp_exclude = [
-            ".+/[+]/components/cronet/ios/.+",
-        ],
-    ),
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-arm64-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-arm64-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-asan-arm-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-kitkat-arm-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-lollipop-arm-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-marshmallow-arm64-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-x86-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-x86-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-x86-dbg-10-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-x86-dbg-11-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-x86-dbg-oreo-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-cronet-x86-dbg-pie-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-deterministic-dbg",
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-)
-
-try_.chromium_android_builder(
-    name = "android-deterministic-rel",
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-)
-
-try_.chromium_android_builder(
-    name = "android-inverse-fieldtrials-pie-x86-fyi-rel",
-)
-
-try_.chromium_android_orchestrator_pair(
-    name = "android-marshmallow-arm64-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    use_java_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    orchestrator_cores = 4,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 64 if settings.is_main else 32,
-    compilator_goma_jobs = goma.jobs.J300,
-    compilator_name = "android-marshmallow-arm64-rel-compilator",
-)
-
-try_.chromium_android_builder(
-    name = "android-marshmallow-arm64-rel-rts",
-    builderless = not settings.is_main,
-    cores = 32 if settings.is_main else 16,
-    goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
-    ssd = True,
-    use_java_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-    # TODO(crbug/1202741)
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_android_orchestrator_pair(
-    name = "android-marshmallow-x86-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    use_java_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    orchestrator_cores = 4,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 32,
-    compilator_goma_jobs = goma.jobs.J300,
-    compilator_name = "android-marshmallow-x86-rel-compilator",
-)
-
-try_.chromium_android_builder(
-    name = "android-marshmallow-x86-rel-non-cq",
-)
-
-try_.chromium_android_builder(
-    name = "android-opus-arm-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-oreo-arm64-cts-networkservice-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-oreo-arm64-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-pie-arm64-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = False,
-    cores = 16,
-    goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/android/features/vr/.+",
-            ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+",
-            ".+/[+]/chrome/android/javatests/src/org/chromium/chrome/browser/vr/.+",
-            ".+/[+]/chrome/browser/android/vr/.+",
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-            ".+/[+]/device/vr/android/.+",
-            ".+/[+]/third_party/gvr-android-sdk/.+",
-            ".+/[+]/third_party/arcore-android-sdk/.+",
-            ".+/[+]/third_party/arcore-android-sdk-client/.+",
-        ],
-    ),
-)
-
-# TODO(crbug/1182468) Remove when experiment is done.
-try_.chromium_android_builder(
-    name = "android-pie-arm64-coverage-experimental-rel",
-    builderless = True,
-    cores = 16,
-    goma_jobs = goma.jobs.J300,
-    ssd = True,
-    main_list_view = "try",
-    use_clang_coverage = True,
-    tryjob = try_.job(
-        experiment_percentage = 3,
-    ),
-)
-
-try_.chromium_android_orchestrator_pair(
-    name = "android-pie-arm64-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    orchestrator_cores = 4,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 32,
-    compilator_goma_jobs = goma.jobs.J300,
-    compilator_name = "android-pie-arm64-rel-compilator",
-)
-
-try_.chromium_android_builder(
-    name = "android-pie-arm64-rel-rts",
-    builderless = not settings.is_main,
-    cores = 16,
-    goma_jobs = goma.jobs.J300,
-    ssd = True,
-    main_list_view = "try",
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-    # TODO(crbug/1202741)
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_android_builder(
-    name = "android-pie-x86-rel",
-    goma_jobs = goma.jobs.J150,
-)
-
-# TODO(crbug/1182468) Remove when coverage is enabled on CQ.
-try_.chromium_android_builder(
-    name = "android-pie-arm64-coverage-rel",
-    cores = 16,
-    goma_jobs = goma.jobs.J300,
-    ssd = True,
-    use_clang_coverage = True,
-)
-
-try_.chromium_android_builder(
-    name = "android-10-x86-fyi-rel-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-pie-arm64-wpt-rel-non-cq",
-)
-
-try_.chromium_android_builder(
-    name = "android-web-platform-pie-x86-fyi-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-weblayer-10-x86-rel-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-weblayer-marshmallow-x86-rel-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-weblayer-pie-x86-rel-tests",
-)
-
-try_.chromium_android_builder(
-    name = "android-weblayer-pie-x86-wpt-fyi-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-weblayer-pie-x86-wpt-smoketest",
-)
-
-try_.chromium_android_builder(
-    name = "android-webview-pie-x86-wpt-fyi-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android-webview-marshmallow-arm64-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-webview-nougat-arm64-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-webview-oreo-arm64-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-webview-pie-arm64-dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android-webview-pie-arm64-fyi-rel",
-)
-
-try_.chromium_android_builder(
-    name = "android_archive_rel_ng",
-)
-
-try_.chromium_android_builder(
-    name = "android_arm64_dbg_recipe",
-    goma_jobs = goma.jobs.J300,
-)
-
-try_.chromium_android_builder(
-    name = "android_blink_rel",
-)
-
-try_.chromium_android_builder(
-    name = "android_cfi_rel_ng",
-    cores = 32,
-)
-
-try_.chromium_android_builder(
-    name = "android_clang_dbg_recipe",
-    goma_jobs = goma.jobs.J300,
-)
-
-try_.chromium_android_builder(
-    name = "android_compile_dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
-    tryjob = try_.job(),
-    # TODO(crbug/1202741)
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_android_builder(
-    name = "android_compile_x64_dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    cores = 16,
-    ssd = True,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+",
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-            ".+/[+]/sandbox/linux/seccomp-bpf/.+",
-            ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+",
-            ".+/[+]/sandbox/linux/system_headers/.+",
-            ".+/[+]/sandbox/linux/tests/.+",
-            ".+/[+]/third_party/gvr-android-sdk/.+",
-        ],
-    ),
-)
-
-try_.chromium_android_builder(
-    name = "android_compile_x86_dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    cores = 16,
-    ssd = True,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+",
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-            ".+/[+]/sandbox/linux/seccomp-bpf/.+",
-            ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+",
-            ".+/[+]/sandbox/linux/system_headers/.+",
-            ".+/[+]/sandbox/linux/tests/.+",
-            ".+/[+]/third_party/gvr-android-sdk/.+",
-        ],
-    ),
-)
-
-try_.chromium_android_builder(
-    name = "android_cronet",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-    # TODO(crbug/1202741)
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_android_builder(
-    name = "android_mojo",
-)
-
-try_.chromium_android_builder(
-    name = "android_n5x_swarming_dbg",
-)
-
-try_.chromium_android_builder(
-    name = "android_unswarmed_pixel_aosp",
-)
-
-try_.chromium_android_builder(
-    name = "cast_shell_android",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-    # TODO(crbug/1202741)
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_android_builder(
-    name = "linux_android_dbg_ng",
-)
-
-try_.chromium_android_builder(
-    name = "try-nougat-phone-tester",
-)
-
-try_.chromium_angle_builder(
-    name = "android-angle-chromium-try",
-    os = os.LINUX_BIONIC_REMOVE,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "android-angle-try",
-    os = os.LINUX_BIONIC_REMOVE,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "android_angle_rel_ng",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-try_.chromium_angle_builder(
-    name = "fuchsia-angle-rel",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-try_.chromium_angle_builder(
-    name = "fuchsia-angle-try",
-    os = os.LINUX_BIONIC_REMOVE,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "linux-angle-rel",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-try_.chromium_angle_builder(
-    name = "linux-angle-chromium-try",
-    os = os.LINUX_BIONIC_REMOVE,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "linux-angle-try",
-    os = os.LINUX_BIONIC_REMOVE,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "mac-angle-chromium-try",
-    cores = None,
-    os = os.MAC_ANY,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "win-angle-chromium-x64-try",
-    os = os.WINDOWS_ANY,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "win-angle-chromium-x86-try",
-    os = os.WINDOWS_ANY,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_angle_builder(
-    name = "win-angle-x64-try",
-    os = os.WINDOWS_ANY,
-    executable = "recipe:angle_chromium_trybot",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "chromeos-amd64-generic-cfi-thin-lto-rel",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "chromeos-amd64-generic-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/content/gpu/.+",
-            ".+/[+]/media/.+",
-        ],
-    ),
-)
-
-try_.chromium_chromiumos_orchestrator_pair(
-    name = "chromeos-amd64-generic-rel",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    main_list_view = "try",
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 16,
-    compilator_name = "chromeos-amd64-generic-rel-compilator",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "chromeos-arm-generic-dbg",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "chromeos-arm-generic-rel",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    bootstrap = True,
-    mirrors = ["ci/chromeos-arm-generic-rel"],
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    os = os.LINUX_BIONIC_REMOVE,
-    tryjob = try_.job(),
-)
-
-try_.chromium_chromiumos_builder(
-    name = "lacros-amd64-generic-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-try_.chromium_chromiumos_builder(
-    name = "lacros-arm-generic-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-chromeos-compile-dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    os = os.LINUX_BIONIC_REMOVE,
-    tryjob = try_.job(),
-)
-
-try_.chromium_chromiumos_builder(
-    name = "chromeos-kevin-compile-rel",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "chromeos-kevin-rel",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/build/chromeos/.+",
-            ".+/[+]/build/config/chromeos/.*",
-            ".+/[+]/chromeos/CHROMEOS_LKGM",
-        ],
-    ),
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-chromeos-inverse-fieldtrials-fyi-rel",
-)
-
-try_.chromium_chromiumos_orchestrator_pair(
-    name = "linux-chromeos-rel",
-    branch_selector = branches.CROS_LTS_MILESTONE,
-    main_list_view = "try",
-    use_clang_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 32,
-    compilator_goma_jobs = goma.jobs.J300,
-    compilator_name = "linux-chromeos-rel-compilator",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-chromeos-js-code-coverage",
-    use_clang_coverage = True,
-    use_javascript_coverage = True,
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-lacros-dbg",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-lacros-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    cores = 16,
-    ssd = True,
-    goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
-    tryjob = try_.job(),
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-lacros-rel-code-coverage",
-    cores = 16,
-    ssd = True,
-    goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
-    use_clang_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    os = os.LINUX_BIONIC_REMOVE,
-    tryjob = try_.job(
-        experiment_percentage = 3,
-    ),
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-lacros-rel-rts",
-    builderless = False,
-    cores = 16,
-    ssd = True,
-    goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
-    os = os.LINUX_BIONIC_REMOVE,
-    tryjob = try_.job(
-        experiment_percentage = 1,
-    ),
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-chromeos-dbg",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-chromeos-annotator-rel",
-)
-
-try_.chromium_chromiumos_builder(
-    name = "linux-cfm-rel",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chromeos/components/chromebox_for_meetings/.+",
-            ".+/[+]/chromeos/dbus/chromebox_for_meetings/.+",
-            ".+/[+]/chromeos/services/chromebox_for_meetings/.+",
-            ".+/[+]/chrome/browser/chromeos/chromebox_for_meetings/.+",
-            ".+/[+]/chrome/browser/resources/chromeos/chromebox_for_meetings/.+",
-            ".+/[+]/chrome/browser/ui/webui/chromeos/chromebox_for_meetings/.+",
-            ".+/[+]/chrome/test/data/webui/chromeos/chromebox_for_meetings/.+",
-        ],
-    ),
-)
-
-try_.chromium_dawn_builder(
-    name = "dawn-linux-x64-deps-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    os = os.LINUX_BIONIC_REMOVE,
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/gpu/.+",
-            ".+/[+]/testing/buildbot/chromium.dawn.json",
-            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
-            ".+/[+]/third_party/dawn/.+",
-            ".+/[+]/third_party/webgpu-cts/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/features.gni",
-        ],
-    ),
-)
-
-try_.chromium_dawn_builder(
-    name = "dawn-mac-x64-deps-rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    main_list_view = "try",
-    os = os.MAC_ANY,
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/gpu/.+",
-            ".+/[+]/testing/buildbot/chromium.dawn.json",
-            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
-            ".+/[+]/third_party/dawn/.+",
-            ".+/[+]/third_party/webgpu-cts/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/features.gni",
-        ],
-    ),
-)
-
-try_.chromium_dawn_builder(
-    name = "dawn-win10-x64-deps-rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    main_list_view = "try",
-    os = os.WINDOWS_ANY,
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/gpu/.+",
-            ".+/[+]/testing/buildbot/chromium.dawn.json",
-            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
-            ".+/[+]/third_party/dawn/.+",
-            ".+/[+]/third_party/webgpu-cts/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/features.gni",
-        ],
-    ),
-)
-
-try_.chromium_dawn_builder(
-    name = "dawn-win10-x86-deps-rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    main_list_view = "try",
-    os = os.WINDOWS_ANY,
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/gpu/.+",
-            ".+/[+]/testing/buildbot/chromium.dawn.json",
-            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
-            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
-            ".+/[+]/third_party/dawn/.+",
-            ".+/[+]/third_party/webgpu-cts/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/features.gni",
-        ],
-    ),
-)
-
-try_.chromium_dawn_builder(
-    name = "linux-dawn-rel",
-    os = os.LINUX_BIONIC_REMOVE,
-)
-
-try_.chromium_dawn_builder(
-    name = "mac-dawn-rel",
-    os = os.MAC_ANY,
-)
-
-try_.chromium_dawn_builderless_builder(
-    name = "dawn-try-mac-amd-exp",
-    os = os.MAC_ANY,
-    pool = "luci.chromium.gpu.mac.retina.amd.try",
-)
-
-try_.chromium_dawn_builderless_builder(
-    name = "dawn-try-mac-intel-exp",
-    os = os.MAC_ANY,
-    pool = "luci.chromium.gpu.mac.mini.intel.uhd630.try",
-)
-
-try_.chromium_dawn_builder(
-    name = "win-dawn-rel",
-    os = os.WINDOWS_ANY,
-)
-
-try_.chromium_dawn_builder(
-    name = "dawn-try-win10-x86-rel",
-    os = os.WINDOWS_ANY,
-)
-
-try_.chromium_dawn_builder(
-    name = "dawn-try-win10-x64-asan-rel",
-    os = os.WINDOWS_ANY,
-)
-
-try_.chromium_linux_builder(
-    name = "cast_shell_audio_linux",
-)
-
-try_.chromium_linux_builder(
-    name = "cast_shell_linux",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_linux_builder(
-    name = "cast_shell_linux_dbg",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chromecast/.+",
-        ],
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "cast_shell_linux_arm64",
-    branch_selector = branches.MAIN,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chromecast/.+",
-        ],
-    ),
-    os = os.LINUX_BIONIC,
-)
-
-try_.chromium_linux_builder(
-    name = "cast-binary-size",
-    builderless = True,
-    executable = "recipe:binary_size_cast_trybot",
-    properties = {
-        "$build/binary_size": {
-            "analyze_targets": [
-                "//chromecast:cast_shell",
-            ],
-            "compile_targets": [
-                "cast_shell",
-            ],
-        },
-    },
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-binary-size",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = True,
-    executable = "recipe:binary_size_fuchsia_trybot",
-    properties = {
-        "$build/binary_size": {
-            "analyze_targets": [
-                "//fuchsia/release:fuchsia_sizes",
-            ],
-            "compile_targets": [
-                "fuchsia_sizes",
-            ],
-        },
-    },
-    tryjob = try_.job(
-        experiment_percentage = 20,
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-arm64-cast",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chromecast/.+",
-        ],
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-compile-x64-dbg",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/base/fuchsia/.+",
-            ".+/[+]/fuchsia/.+",
-            ".+/[+]/media/fuchsia/.+",
-        ],
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-deterministic-dbg",
-    executable = "recipe:swarming/deterministic_build",
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-fyi-arm64-dbg",
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-fyi-arm64-femu",
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-fyi-arm64-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-fyi-x64-dbg",
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-fyi-x64-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia-x64-cast",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia_arm64",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia_x64",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_linux_builder(
-    name = "layout_test_leak_detection",
-)
-
-try_.chromium_linux_builder(
-    name = "leak_detection_linux",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-1mbu-compile-fyi-rel",
-    builderless = False,
-    goma_jobs = goma.jobs.J150,
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-    properties = {
-        "bot_update_experiments": [
-            "no_sync",
-        ],
-    },
-)
-
-try_.chromium_linux_builder(
-    name = "linux-annotator-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-autofill-assistant",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-bfcache-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-bionic-rel",
-    goma_jobs = goma.jobs.J150,
-    os = os.LINUX_BIONIC,
-)
-
-try_.chromium_linux_builder(
-    name = "linux-blink-heap-concurrent-marking-tsan-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-blink-heap-verification-try",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-blink-v8-oilpan",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-blink-web-tests-force-accessibility-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-clang-tidy-dbg",
-    executable = "recipe:tricium_clang_tidy_wrapper",
-    goma_jobs = goma.jobs.J150,
-)
-
-try_.chromium_linux_builder(
-    name = "linux-clang-tidy-rel",
-    executable = "recipe:tricium_clang_tidy_wrapper",
-    goma_jobs = goma.jobs.J150,
-)
-
-try_.chromium_linux_builder(
-    name = "linux-dcheck-off-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-example-builder",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-extended-tracing-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-gcc-rel",
-    goma_backend = None,
-)
-
-try_.chromium_linux_builder(
-    name = "linux-headless-shell-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-inverse-fieldtrials-fyi-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-mbi-mode-per-render-process-host-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-mbi-mode-per-site-instance-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-lacros-fyi-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-lacros-version-skew-fyi",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-layout-tests-edit-ng",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-libfuzzer-asan-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    executable = "recipe:chromium_libfuzzer_trybot",
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_linux_builder(
-    name = "linux-perfetto-rel",
-    tryjob = try_.job(
-        experiment_percentage = 100,
-        location_regexp = [
-            ".+/[+]/base/trace_event/.+",
-            ".+/[+]/base/tracing/.+",
-            ".+/[+]/components/tracing/.+",
-            ".+/[+]/content/browser/tracing/.+",
-            ".+/[+]/services/tracing/.+",
-        ],
-    ),
-)
-
-try_.chromium_linux_orchestrator_pair(
-    name = "linux-rel",
-    bootstrap = True,
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    use_clang_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 16,
-    compilator_goma_jobs = goma.jobs.J150,
-    compilator_name = "linux-rel-compilator",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-wayland-rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_linux_builder(
-    name = "linux-trusty-rel",
-    goma_jobs = goma.jobs.J150,
-    os = os.LINUX_TRUSTY,
-)
-
-try_.chromium_linux_builder(
-    name = "linux-xenial-rel",
-    goma_jobs = goma.jobs.J150,
-    os = os.LINUX_XENIAL,
-)
-
-try_.chromium_linux_builder(
-    name = "linux-viz-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-webkit-msan-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-wpt-fyi-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-wpt-identity-fyi-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux-wpt-input-fyi-rel",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_analysis",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_archive_rel_ng",
-)
-
-try_.chromium_linux_orchestrator_pair(
-    name = "linux_chromium_asan_rel_ng",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 16,
-    compilator_goma_jobs = goma.jobs.J150,
-    compilator_name = "linux_chromium_asan_rel_ng-compilator",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_asan_rel_ng_rts",
-    goma_jobs = goma.jobs.J150,
-    ssd = True,
-    main_list_view = "try",
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_cfi_rel_ng",
-    cores = 32,
-    # TODO(thakis): Remove once https://crbug.com/927738 is resolved.
-    execution_timeout = 7 * time.hour,
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_chromeos_asan_rel_ng",
-    goma_jobs = goma.jobs.J150,
-    # TODO(crbug/1144484): Remove this timeout once we figure out the
-    # regression in compiler or toolchain.
-    execution_timeout = 7 * time.hour,
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_chromeos_msan_rel_ng",
-    goma_jobs = goma.jobs.J150,
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_clobber_deterministic",
-    executable = "recipe:swarming/deterministic_build",
-    execution_timeout = 6 * time.hour,
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_clobber_rel_ng",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_compile_dbg_32_ng",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_compile_dbg_ng",
-    branch_selector = branches.STANDARD_MILESTONE,
-    builderless = not settings.is_main,
-    caches = [
-        swarming.cache(
-            name = "builder",
-            path = "linux_debug",
-        ),
-    ],
-    goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_compile_rel_ng",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_dbg_ng",
-    branch_selector = branches.STANDARD_MILESTONE,
-    caches = [
-        swarming.cache(
-            name = "builder",
-            path = "linux_debug",
-        ),
-    ],
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/build/.*check_gn_headers.*",
-        ],
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_msan_rel_ng",
-    execution_timeout = 6 * time.hour,
-    goma_jobs = goma.jobs.J150,
-)
-
-try_.chromium_linux_orchestrator_pair(
-    name = "linux_chromium_tsan_rel_ng",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 16,
-    compilator_goma_jobs = goma.jobs.J150,
-    compilator_name = "linux_chromium_tsan_rel_ng-compilator",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_tsan_rel_ng_rts",
-    builderless = not settings.is_main,
-    goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "linux_chromium_ubsan_rel_ng",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_layout_tests_layout_ng_disabled",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/third_party/blink/renderer/core/editing/.+",
-            ".+/[+]/third_party/blink/renderer/core/layout/.+",
-            ".+/[+]/third_party/blink/renderer/core/paint/.+",
-            ".+/[+]/third_party/blink/renderer/core/svg/.+",
-            ".+/[+]/third_party/blink/renderer/platform/fonts/shaping/.+",
-            ".+/[+]/third_party/blink/renderer/platform/graphics/.+",
-            ".+/[+]/third_party/blink/web_tests/.+",
-        ],
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "linux_mojo",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_mojo_chromeos",
-)
-
-try_.chromium_linux_builder(
-    name = "linux_upload_clang",
-    builderless = True,
-    cores = 32,
-    executable = "recipe:chromium_upload_clang",
-    goma_backend = None,
-    os = os.LINUX_TRUSTY,
-)
-
-try_.chromium_linux_builder(
-    name = "linux_vr",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-        ],
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "network_service_linux",
-)
-
-try_.chromium_linux_builder(
-    name = "tricium-metrics-analysis",
-    executable = "recipe:tricium_metrics",
-)
-
-try_.chromium_linux_builder(
-    name = "tricium-oilpan-analysis",
-    executable = "recipe:tricium_oilpan",
-)
-
-try_.chromium_linux_builder(
-    name = "tricium-simple",
-    executable = "recipe:tricium_simple",
-)
-
-try_.chromium_mac_builder(
-    name = "mac-osxbeta-rel",
-    os = os.MAC_DEFAULT,
-)
-
-try_.chromium_mac_builder(
-    name = "mac-inverse-fieldtrials-fyi-rel",
-    os = os.MAC_DEFAULT,
-)
-
-try_.chromium_mac_builder(
-    name = "mac-rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    builderless = not settings.is_main,
-    use_clang_coverage = True,
-    goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
-    os = os.MAC_DEFAULT,
-    tryjob = try_.job(),
-)
-
-try_.chromium_mac_orchestrator_pair(
-    name = "mac-rel-orchestrator",
-    main_list_view = "try",
-    use_clang_coverage = True,
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(
-        experiment_percentage = 1,
-    ),
-    compilator_goma_jobs = goma.jobs.J150,
-    compilator_os = os.MAC_DEFAULT,
-    compilator_name = "mac-rel-compilator",
-)
-
-try_.chromium_mac_orchestrator_pair(
-    name = "mac11-arm64-rel",
-    main_list_view = "try",
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(
-        experiment_percentage = 50,
-    ),
-    compilator_goma_jobs = goma.jobs.J150,
-    compilator_os = os.MAC_11,
-    compilator_name = "mac11-arm64-rel-compilator",
-    # TODO (crbug/1271287): Revert when root issue is fixed
-    compilator_grace_period = 4 * time.minute,
-)
-
-# NOTE: the following trybots aren't sensitive to Mac version on which
-# they are built, hence no additional dimension is specified.
-# The 10.xx version translates to which bots will run isolated tests.
-try_.chromium_mac_builder(
-    name = "mac_chromium_10.11_rel_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_10.12_rel_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_10.13_rel_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_10.14_rel_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_10.15_rel_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_11.0_rel_ng",
-    builderless = False,
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_archive_rel_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_asan_rel_ng",
-    goma_jobs = goma.jobs.J150,
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_compile_dbg_ng",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    goma_jobs = goma.jobs.J150,
-    os = os.MAC_DEFAULT,
-    main_list_view = "try",
-    tryjob = try_.job(),
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_compile_rel_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_chromium_dbg_ng",
-)
-
-try_.chromium_mac_builder(
-    name = "mac_upload_clang",
-    builderless = False,
-    executable = "recipe:chromium_upload_clang",
-    execution_timeout = 6 * time.hour,
-    goma_backend = None,  # Does not use Goma.
-)
-
-try_.chromium_mac_builder(
-    name = "mac_upload_clang_arm",
-    builderless = False,
-    executable = "recipe:chromium_upload_clang",
-    execution_timeout = 6 * time.hour,
-    goma_backend = None,  # Does not use Goma.
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-catalyst",
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-device",
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-simulator",
-    branch_selector = branches.STANDARD_MILESTONE,
-    check_for_flakiness = True,
-    main_list_view = "try",
-    use_clang_coverage = True,
-    coverage_exclude_sources = "ios_test_files_and_test_utils",
-    coverage_test_types = ["overall", "unit"],
-    tryjob = try_.job(),
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-simulator-cronet",
-    branch_selector = branches.STANDARD_MILESTONE,
-    check_for_flakiness = True,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/components/cronet/.+",
-            ".+/[+]/components/grpc_support/.+",
-            ".+/[+]/ios/.+",
-        ],
-        location_regexp_exclude = [
-            ".+/[+]/components/cronet/android/.+",
-        ],
-    ),
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-simulator-full-configs",
-    branch_selector = branches.STANDARD_MILESTONE,
-    check_for_flakiness = True,
-    main_list_view = "try",
-    use_clang_coverage = True,
-    coverage_exclude_sources = "ios_test_files_and_test_utils",
-    coverage_test_types = ["overall", "unit"],
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/ios/.+",
-        ],
-    ),
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-simulator-inverse-fieldtrials-fyi",
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-simulator-multi-window",
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-simulator-noncq",
-    xcode = xcode.x13main,
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/third_party/crashpad/crashpad/.+",
-        ],
-    ),
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios14-beta-simulator",
-    os = os.MAC_11,
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios14-sdk-simulator",
-    os = os.MAC_11,
-    cpu = cpu.ARM64,
-)
-
-try_.chromium_updater_mac_builder(
-    name = "mac-updater-try-builder-dbg",
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/updater/.+",
-        ],
-    ),
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios15-beta-simulator",
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios15-sdk-simulator",
-    xcode = xcode.x13latestbeta,
-)
-
-try_.chromium_updater_mac_builder(
-    name = "mac-updater-try-builder-rel",
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/updater/.+",
-        ],
-    ),
-)
-
-try_.chromium_updater_win_builder(
-    name = "win-updater-try-builder-dbg",
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/updater/.+",
-        ],
-    ),
-)
-
-try_.chromium_updater_win_builder(
-    name = "win-updater-try-builder-rel",
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/updater/.+",
-        ],
-    ),
-)
-
-try_.chromium_rust_builder(
-    name = "linux-rust-x64-rel",
-)
-
-try_.chromium_rust_builder(
-    name = "android-rust-arm-rel",
-)
-
-try_.chromium_win_builder(
-    name = "win-annotator-rel",
-)
-
-try_.chromium_win_builder(
-    name = "win-asan",
-    goma_jobs = goma.jobs.J150,
-)
-
-try_.chromium_win_builder(
-    name = "win-celab-try-rel",
-    executable = "recipe:celab",
-    properties = {
-        "exclude": "chrome_only",
-        "pool_name": "celab-chromium-try",
-        "pool_size": 20,
-        "tests": "*",
-    },
-)
-
-try_.chromium_win_builder(
-    name = "win-libfuzzer-asan-rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    builderless = False,
-    executable = "recipe:chromium_libfuzzer_trybot",
-    main_list_view = "try",
-    os = os.WINDOWS_ANY,
-    tryjob = try_.job(),
-)
-
-try_.chromium_win_builder(
-    name = "win_archive",
-)
-
-try_.chromium_win_builder(
-    name = "win_chromium_compile_dbg_ng",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
-    tryjob = try_.job(),
-    builderless = False,
-    cores = 16,
-    ssd = True,
-)
-
-try_.chromium_win_builder(
-    name = "win_chromium_compile_rel_ng",
-)
-
-try_.chromium_win_builder(
-    name = "win_chromium_dbg_ng",
-)
-
-try_.chromium_win_builder(
-    name = "win_chromium_x64_rel_ng",
-)
-
-try_.chromium_win_builder(
-    name = "win_mojo",
-)
-
-try_.chromium_win_builder(
-    name = "win_upload_clang",
-    builderless = False,
-    cores = 32,
-    executable = "recipe:chromium_upload_clang",
-    goma_backend = None,
-    os = os.WINDOWS_ANY,
-    execution_timeout = 6 * time.hour,
-)
-
-try_.chromium_win_builder(
-    name = "win_x64_archive",
-)
-
-try_.chromium_win_builder(
-    name = "win10_chromium_x64_20h2_fyi_rel_ng",
-    builderless = False,
-    use_clang_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    os = os.WINDOWS_10_20h2,
-)
-
-try_.chromium_win_builder(
-    name = "win10_chromium_x64_dbg_ng",
-    os = os.WINDOWS_10,
-)
-
-try_.chromium_win_builder(
-    name = "win10_chromium_inverse_fieldtrials_x64_fyi_rel_ng",
-    os = os.WINDOWS_10,
-)
-
-try_.chromium_win_orchestrator_pair(
-    name = "win10_chromium_x64_rel_ng",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    use_clang_coverage = True,
-    coverage_test_types = ["unit", "overall"],
-    main_list_view = "try",
-    orchestrator_cores = 2,
-    orchestrator_tryjob = try_.job(),
-    compilator_cores = 32,
-    compilator_goma_jobs = goma.jobs.J300,
-    compilator_name = "win10_chromium_x64_rel_ng-compilator",
-    # TODO (crbug/1271287): Revert when root issue is fixed
-    compilator_grace_period = 3 * time.minute,
-)
-
-try_.chromium_win_builder(
-    name = "win10_chromium_x64_rel_ng_exp",
-    builderless = False,
-    os = os.WINDOWS_ANY,
-)
-
-try_.chromium_win_builder(
-    name = "win7-rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    cores = 16,
-    execution_timeout = 4 * time.hour + 30 * time.minute,
-    goma_jobs = goma.jobs.J300,
-    main_list_view = "try",
-    ssd = True,
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/sandbox/win/.+",
-            ".+/[+]/sandbox/policy/win/.+",
-        ],
-    ),
-)
-
-try_.cipd_3pp_builder(
-    name = "3pp-linux-amd64-packager",
-    os = os.LINUX_XENIAL_OR_BIONIC_SWITCH_TO_DEFAULT,
-    builderless = False,
-    properties = {
-        "$build/chromium_3pp": {
-            "platform": "linux-amd64",
-            "package_prefix": "chromium_3pp",
-            "preprocess": [{
-                "name": "third_party/android_deps",
-                "cmd": [
-                    "{CHECKOUT}/src/third_party/android_deps/fetch_all.py",
-                    "-v",
-                    "--ignore-vulnerabilities",
-                ],
-            }],
-            "gclient_config": "chromium",
-            "gclient_apply_config": ["android"],
-        },
-    },
-    tryjob = try_.job(
-        location_regexp = [
-            # Enable for CLs touching files under "3pp" directories which are
-            # two level deep or more from the repo root.
-            ".+/[+]/.+/3pp/.+",
-        ],
-    ),
-)
-
-try_.cipd_3pp_builder(
-    name = "3pp-mac-amd64-packager",
-    os = os.MAC_DEFAULT,
-    builderless = True,
-    cores = None,
-    properties = {
-        "$build/chromium_3pp": {
-            "platform": "mac-amd64",
-            "package_prefix": "chromium_3pp",
-            "gclient_config": "chromium",
-        },
-    },
-)
-
-try_.gpu_chromium_android_builder(
-    name = "android_optional_gpu_tests_rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    goma_jobs = goma.jobs.J150,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/cc/.+",
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-            ".+/[+]/components/viz/.+",
-            ".+/[+]/content/test/gpu/.+",
-            ".+/[+]/gpu/.+",
-            ".+/[+]/media/audio/.+",
-            ".+/[+]/media/base/.+",
-            ".+/[+]/media/capture/.+",
-            ".+/[+]/media/filters/.+",
-            ".+/[+]/media/gpu/.+",
-            ".+/[+]/media/mojo/.+",
-            ".+/[+]/media/renderers/.+",
-            ".+/[+]/media/video/.+",
-            ".+/[+]/services/viz/.+",
-            ".+/[+]/testing/trigger_scripts/.+",
-            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
-            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/.+",
-        ],
-    ),
-)
-
-try_.gpu_chromium_linux_builder(
-    name = "linux_optional_gpu_tests_rel",
-    branch_selector = branches.STANDARD_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-            ".+/[+]/content/test/gpu/.+",
-            ".+/[+]/gpu/.+",
-            ".+/[+]/media/audio/.+",
-            ".+/[+]/media/base/.+",
-            ".+/[+]/media/capture/.+",
-            ".+/[+]/media/filters/.+",
-            ".+/[+]/media/gpu/.+",
-            ".+/[+]/media/mojo/.+",
-            ".+/[+]/media/renderers/.+",
-            ".+/[+]/media/video/.+",
-            ".+/[+]/testing/buildbot/chromium.gpu.fyi.json",
-            ".+/[+]/testing/trigger_scripts/.+",
-            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
-            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/.+",
-        ],
-    ),
-)
-
-try_.gpu_chromium_mac_builder(
-    name = "mac_optional_gpu_tests_rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    main_list_view = "try",
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-            ".+/[+]/content/test/gpu/.+",
-            ".+/[+]/gpu/.+",
-            ".+/[+]/media/audio/.+",
-            ".+/[+]/media/base/.+",
-            ".+/[+]/media/capture/.+",
-            ".+/[+]/media/filters/.+",
-            ".+/[+]/media/gpu/.+",
-            ".+/[+]/media/mojo/.+",
-            ".+/[+]/media/renderers/.+",
-            ".+/[+]/media/video/.+",
-            ".+/[+]/services/shape_detection/.+",
-            ".+/[+]/testing/buildbot/chromium.gpu.fyi.json",
-            ".+/[+]/testing/trigger_scripts/.+",
-            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
-            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/.+",
-        ],
-    ),
-)
-
-try_.gpu_chromium_win_builder(
-    name = "win_optional_gpu_tests_rel",
-    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
-    builderless = True,
-    main_list_view = "try",
-    os = os.WINDOWS_DEFAULT,
-    tryjob = try_.job(
-        location_regexp = [
-            ".+/[+]/chrome/browser/vr/.+",
-            ".+/[+]/content/browser/xr/.+",
-            ".+/[+]/content/test/gpu/.+",
-            ".+/[+]/device/vr/.+",
-            ".+/[+]/gpu/.+",
-            ".+/[+]/media/audio/.+",
-            ".+/[+]/media/base/.+",
-            ".+/[+]/media/capture/.+",
-            ".+/[+]/media/filters/.+",
-            ".+/[+]/media/gpu/.+",
-            ".+/[+]/media/mojo/.+",
-            ".+/[+]/media/renderers/.+",
-            ".+/[+]/media/video/.+",
-            ".+/[+]/testing/buildbot/chromium.gpu.fyi.json",
-            ".+/[+]/testing/trigger_scripts/.+",
-            ".+/[+]/third_party/blink/renderer/modules/vr/.+",
-            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
-            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
-            ".+/[+]/third_party/blink/renderer/modules/xr/.+",
-            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
-            ".+/[+]/tools/clang/scripts/update.py",
-            ".+/[+]/ui/gl/.+",
-        ],
-    ),
-)
-
-try_.chromium_linux_builder(
-    name = "linux-stable-filter-rel",
-    builderless = False,
-    goma_jobs = goma.jobs.J150,
-    use_clang_coverage = True,
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_linux_builder(
-    name = "linux-stable-filter-combined-rel",
-    builderless = False,
-    goma_jobs = goma.jobs.J150,
-    use_clang_coverage = True,
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-# RTS builders (https://crbug.com/1203048)
-try_.chromium_linux_builder(
-    name = "linux-rel-rts",
-    builderless = False,
-    goma_jobs = goma.jobs.J150,
-    use_clang_coverage = True,
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_mac_builder(
-    name = "mac-rel-rts",
-    builderless = False,
-    use_clang_coverage = True,
-    goma_jobs = goma.jobs.J150,
-)
-
-try_.chromium_win_builder(
-    name = "win10_chromium_x64_rel_ng_rts",
-    goma_jobs = goma.jobs.J150,
-    use_clang_coverage = True,
-    builderless = False,
-    cores = 16,
-)
-
-try_.chromium_android_builder(
-    name = "android-marshmallow-x86-rel-rts",
-    goma_jobs = goma.jobs.J300,
-    builderless = False,
-    cores = 16,
-    tryjob = try_.job(
-        experiment_percentage = 5,
-    ),
-    ssd = True,
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_linux_builder(
-    name = "fuchsia_x64_rts",
-    builderless = False,
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_chromiumos_builder(
-    name = "chromeos-amd64-generic-rel-rts",
-    builderless = False,
-    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
-)
-
-try_.chromium_mac_ios_builder(
-    name = "ios-simulator-rts",
-    use_clang_coverage = True,
-    coverage_exclude_sources = "ios_test_files_and_test_utils",
-    coverage_test_types = ["unit"],
-    builderless = False,
-)
-
-try_.infra_builder(
-    name = "linux-bootstrap",
-    bootstrap = True,
-    mirrors = [
-        "ci/linux-bootstrap",
-        "ci/linux-bootstrap-tests",
-    ],
-)
-
-try_.infra_builder(
-    name = "win-bootstrap",
-    bootstrap = True,
-    builderless = True,
-    os = os.WINDOWS_10,
-)
-
-# Errors that this builder would catch would go unnoticed until a project is set
-# up on a branch day or even worse when a branch was turned into an LTS branch,
-# long after the change has been made, so make it a presubmit builder to ensure
-# it's checked with current code. The builder runs in a few minutes and only for
-# infra/config changes, so it won't impose a heavy burden on our capacity.
-try_.presubmit_builder(
-    name = "branch-config-verifier",
-    executable = "recipe:branch_configuration/tester",
-    properties = {
-        "branch_script": "infra/config/scripts/branch.py",
-        "branch_configs": [
-            {
-                "name": branch_type.STANDARD,
-                "branch_types": [branch_type.STANDARD],
-            },
-            {
-                "name": branch_type.DESKTOP_EXTENDED_STABLE,
-                "branch_types": [branch_type.DESKTOP_EXTENDED_STABLE],
-            },
-            {
-                "name": branch_type.CROS_LTS,
-                "branch_types": [branch_type.CROS_LTS],
-            },
-            {
-                "name": "{} + {}".format(branch_type.DESKTOP_EXTENDED_STABLE, branch_type.CROS_LTS),
-                "branch_types": [branch_type.DESKTOP_EXTENDED_STABLE, branch_type.CROS_LTS],
-            },
-        ],
-        "starlark_entry_points": ["infra/config/main.star", "infra/config/dev.star"],
-    },
-    tryjob = try_.job(
-        location_regexp = [r".+/[+]/infra/config/.+"],
-    ),
-)
-
-try_.presubmit_builder(
-    name = "reclient-config-deployment-verifier",
-    executable = "recipe:reclient_config_deploy_check/tester",
-    properties = {
-        "fetch_script": "buildtools/reclient_cfgs/fetch_reclient_cfgs.py",
-        "rbe_project": [
-            {
-                "name": "rbe-chromium-trusted",
-                "cfg_file": [
-                    "buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux.cfg",
-                    "buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_windows.cfg",
-                    "buildtools/reclient_cfgs/nacl/rewrapper_linux.cfg",
-                    "buildtools/reclient_cfgs/nacl/rewrapper_windows.cfg",
-                ],
-            },
-        ],
-    },
-    tryjob = try_.job(
-        location_regexp = [r".+/[+]/tools/clang/scripts/update.py"],
-    ),
-)
-
-try_.presubmit_builder(
-    name = "chromium_presubmit",
-    branch_selector = branches.ALL_BRANCHES,
-    executable = "recipe:presubmit",
-    properties = {
-        "$depot_tools/presubmit": {
-            "runhooks": True,
-            "timeout_s": 480,
-        },
-        "repo_name": "chromium",
-    },
-    tryjob = try_.job(),
-)
+exec("./try/presubmit.star")
+exec("./try/tryserver.blink.star")
+exec("./try/tryserver.chromium.star")
+exec("./try/tryserver.chromium.android.star")
+exec("./try/tryserver.chromium.angle.star")
+exec("./try/tryserver.chromium.chromiumos.star")
+exec("./try/tryserver.chromium.dawn.star")
+exec("./try/tryserver.chromium.linux.star")
+exec("./try/tryserver.chromium.mac.star")
+exec("./try/tryserver.chromium.packager.star")
+exec("./try/tryserver.chromium.rust.star")
+exec("./try/tryserver.chromium.updater.star")
+exec("./try/tryserver.chromium.win.star")
+exec("./try/tryserver.infra.star")
 
 # Used for listing chrome trybots in chromium's commit-queue.cfg without also
 # adding them to chromium's cr-buildbucket.cfg. Note that the recipe these
diff --git a/infra/config/subprojects/chromium/try/presubmit.star b/infra/config/subprojects/chromium/try/presubmit.star
new file mode 100644
index 0000000..82fa491
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/presubmit.star
@@ -0,0 +1,118 @@
+# 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.
+"""Definitions of builders in the chromium.android builder group."""
+
+load("//lib/builders.star", "os")
+load("//lib/branches.star", "branches")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+load("//project.star", "branch_type")
+
+try_.defaults.set(
+    cores = 8,
+    # TODO(gbeaty) After prod freeze, lower this to something sensible for the
+    # actual time these builders take
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    list_view = "presubmit",
+    main_list_view = "try",
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = try_.DEFAULT_POOL,
+    # Default priority for buildbucket is 30, see
+    # https://chromium.googlesource.com/infra/infra/+/bb68e62b4380ede486f65cd32d9ff3f1bbe288e4/appengine/cr-buildbucket/creation.py#42
+    # This will improve our turnaround time for landing infra/config changes
+    # when addressing outages
+    priority = 25,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "presubmit",
+    branch_selector = branches.ALL_BRANCHES,
+    title = "presubmit builders",
+)
+
+def presubmit_builder(*, name, tryjob, **kwargs):
+    """Define a presubmit builder.
+
+    Presubmit builders are builders that run fast checks that don't require
+    building. Their results aren't re-used because they tend to provide guards
+    against generated files being out of date, so they MUST run quickly so that
+    the submit after a CQ dry run doesn't take long.
+    """
+    tryjob_args = {a: getattr(tryjob, a) for a in dir(tryjob)}
+    tryjob_args["disable_reuse"] = True
+    tryjob_args["add_default_excludes"] = False
+    tryjob = try_.job(**tryjob_args)
+    return try_.builder(name = name, tryjob = tryjob, **kwargs)
+
+# Errors that this builder would catch would go unnoticed until a project is set
+# up on a branch day or even worse when a branch was turned into an LTS branch,
+# long after the change has been made, so make it a presubmit builder to ensure
+# it's checked with current code. The builder runs in a few minutes and only for
+# infra/config changes, so it won't impose a heavy burden on our capacity.
+presubmit_builder(
+    name = "branch-config-verifier",
+    executable = "recipe:branch_configuration/tester",
+    properties = {
+        "branch_script": "infra/config/scripts/branch.py",
+        "branch_configs": [
+            {
+                "name": branch_type.STANDARD,
+                "branch_types": [branch_type.STANDARD],
+            },
+            {
+                "name": branch_type.DESKTOP_EXTENDED_STABLE,
+                "branch_types": [branch_type.DESKTOP_EXTENDED_STABLE],
+            },
+            {
+                "name": branch_type.CROS_LTS,
+                "branch_types": [branch_type.CROS_LTS],
+            },
+            {
+                "name": "{} + {}".format(branch_type.DESKTOP_EXTENDED_STABLE, branch_type.CROS_LTS),
+                "branch_types": [branch_type.DESKTOP_EXTENDED_STABLE, branch_type.CROS_LTS],
+            },
+        ],
+        "starlark_entry_points": ["infra/config/main.star", "infra/config/dev.star"],
+    },
+    tryjob = try_.job(
+        location_regexp = [r".+/[+]/infra/config/.+"],
+    ),
+)
+
+presubmit_builder(
+    name = "reclient-config-deployment-verifier",
+    executable = "recipe:reclient_config_deploy_check/tester",
+    properties = {
+        "fetch_script": "buildtools/reclient_cfgs/fetch_reclient_cfgs.py",
+        "rbe_project": [
+            {
+                "name": "rbe-chromium-trusted",
+                "cfg_file": [
+                    "buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_linux.cfg",
+                    "buildtools/reclient_cfgs/chromium-browser-clang/rewrapper_windows.cfg",
+                    "buildtools/reclient_cfgs/nacl/rewrapper_linux.cfg",
+                    "buildtools/reclient_cfgs/nacl/rewrapper_windows.cfg",
+                ],
+            },
+        ],
+    },
+    tryjob = try_.job(
+        location_regexp = [r".+/[+]/tools/clang/scripts/update.py"],
+    ),
+)
+
+presubmit_builder(
+    name = "chromium_presubmit",
+    branch_selector = branches.ALL_BRANCHES,
+    executable = "recipe:presubmit",
+    properties = {
+        "$depot_tools/presubmit": {
+            "runhooks": True,
+            "timeout_s": 480,
+        },
+        "repo_name": "chromium",
+    },
+    tryjob = try_.job(),
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.blink.star b/infra/config/subprojects/chromium/try/tryserver.blink.star
new file mode 100644
index 0000000..363f1362
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.blink.star
@@ -0,0 +1,95 @@
+# 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.
+"""Definitions of builders in the tryserver.blink builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/branches.star", "branches")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+
+try_.defaults.set(
+    builder_group = "tryserver.blink",
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.blink",
+    branch_selector = branches.STANDARD_MILESTONE,
+)
+
+def blink_mac_builder(*, name, **kwargs):
+    kwargs.setdefault("builderless", True)
+    kwargs.setdefault("cores", None)
+    kwargs.setdefault("goma_backend", goma.backend.RBE_PROD)
+    kwargs.setdefault("os", os.MAC_ANY)
+    kwargs.setdefault("ssd", True)
+    return try_.builder(
+        name = name,
+        **kwargs
+    )
+
+try_.builder(
+    name = "linux-blink-optional-highdpi-rel",
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+try_.builder(
+    name = "linux-blink-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    goma_backend = goma.backend.RBE_PROD,
+    main_list_view = "try",
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/cc/.+",
+            ".+/[+]/third_party/blink/renderer/core/paint/.+",
+            ".+/[+]/third_party/blink/renderer/core/svg/.+",
+            ".+/[+]/third_party/blink/renderer/platform/graphics/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "win10.20h2-blink-rel",
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.WINDOWS_ANY,
+    builderless = True,
+)
+
+try_.builder(
+    name = "win7-blink-rel",
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.WINDOWS_ANY,
+    builderless = True,
+)
+
+blink_mac_builder(
+    name = "mac10.12-blink-rel",
+)
+
+blink_mac_builder(
+    name = "mac10.13-blink-rel",
+)
+
+blink_mac_builder(
+    name = "mac10.14-blink-rel",
+)
+
+blink_mac_builder(
+    name = "mac10.15-blink-rel",
+)
+
+blink_mac_builder(
+    name = "mac11.0-blink-rel",
+    builderless = False,
+)
+
+blink_mac_builder(
+    name = "mac11.0.arm64-blink-rel",
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.android.star b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
new file mode 100644
index 0000000..f842f7b
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.android.star
@@ -0,0 +1,510 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.android builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+load("//project.star", "settings")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.android",
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.android",
+    branch_selector = branches.STANDARD_MILESTONE,
+)
+
+try_.builder(
+    name = "android-10-arm64-rel",
+)
+
+try_.builder(
+    name = "android-11-x86-rel",
+)
+
+try_.builder(
+    name = "android-12-x64-fyi-rel",
+)
+
+try_.builder(
+    name = "android-asan",
+)
+
+try_.builder(
+    name = "android-bfcache-rel",
+)
+
+try_.builder(
+    name = "android-binary-size",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    # TODO (kimstephanie): Change to cores = 16 and ssd = True once bots have
+    # landed
+    cores = 16,
+    executable = "recipe:binary_size_trybot",
+    goma_jobs = goma.jobs.J150,
+    main_list_view = "try",
+    properties = {
+        "$build/binary_size": {
+            "analyze_targets": [
+                "//chrome/android:monochrome_public_minimal_apks",
+                "//chrome/android:trichrome_minimal_apks",
+                "//chrome/android:validate_expectations",
+                "//tools/binary_size:binary_size_trybot_py",
+            ],
+            "compile_targets": [
+                "monochrome_public_minimal_apks",
+                "monochrome_static_initializers",
+                "trichrome_minimal_apks",
+                "validate_expectations",
+            ],
+        },
+    },
+    tryjob = try_.job(),
+    # TODO(crbug/1202741)
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+    ssd = True,
+)
+
+try_.builder(
+    name = "android-cronet-arm-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/components/cronet/.+",
+            ".+/[+]/components/grpc_support/.+",
+            ".+/[+]/build/android/.+",
+            ".+/[+]/build/config/android/.+",
+        ],
+        location_regexp_exclude = [
+            ".+/[+]/components/cronet/ios/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "android-cronet-arm64-dbg",
+)
+
+try_.builder(
+    name = "android-cronet-arm64-rel",
+)
+
+try_.builder(
+    name = "android-cronet-asan-arm-rel",
+)
+
+try_.builder(
+    name = "android-cronet-kitkat-arm-rel",
+)
+
+try_.builder(
+    name = "android-cronet-lollipop-arm-rel",
+)
+
+try_.builder(
+    name = "android-cronet-marshmallow-arm64-rel",
+)
+
+try_.builder(
+    name = "android-cronet-x86-dbg",
+)
+
+try_.builder(
+    name = "android-cronet-x86-rel",
+)
+
+try_.builder(
+    name = "android-cronet-x86-dbg-10-tests",
+)
+
+try_.builder(
+    name = "android-cronet-x86-dbg-11-tests",
+)
+
+try_.builder(
+    name = "android-cronet-x86-dbg-oreo-tests",
+)
+
+try_.builder(
+    name = "android-cronet-x86-dbg-pie-tests",
+)
+
+try_.builder(
+    name = "android-deterministic-dbg",
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+)
+
+try_.builder(
+    name = "android-deterministic-rel",
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+)
+
+try_.builder(
+    name = "android-inverse-fieldtrials-pie-x86-fyi-rel",
+)
+
+try_.orchestrator_pair_builders(
+    name = "android-marshmallow-arm64-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    use_java_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    orchestrator_cores = 4,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 64 if settings.is_main else 32,
+    compilator_goma_jobs = goma.jobs.J300,
+    compilator_name = "android-marshmallow-arm64-rel-compilator",
+)
+
+try_.builder(
+    name = "android-marshmallow-arm64-rel-rts",
+    builderless = not settings.is_main,
+    cores = 32 if settings.is_main else 16,
+    goma_jobs = goma.jobs.J300,
+    main_list_view = "try",
+    ssd = True,
+    use_java_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+    # TODO(crbug/1202741)
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+try_.orchestrator_pair_builders(
+    name = "android-marshmallow-x86-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    use_java_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    orchestrator_cores = 4,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 32,
+    compilator_goma_jobs = goma.jobs.J300,
+    compilator_name = "android-marshmallow-x86-rel-compilator",
+)
+
+try_.builder(
+    name = "android-marshmallow-x86-rel-non-cq",
+)
+
+try_.builder(
+    name = "android-opus-arm-rel",
+)
+
+try_.builder(
+    name = "android-oreo-arm64-cts-networkservice-dbg",
+)
+
+try_.builder(
+    name = "android-oreo-arm64-dbg",
+)
+
+try_.builder(
+    name = "android-pie-arm64-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = False,
+    cores = 16,
+    goma_jobs = goma.jobs.J300,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/android/features/vr/.+",
+            ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+",
+            ".+/[+]/chrome/android/javatests/src/org/chromium/chrome/browser/vr/.+",
+            ".+/[+]/chrome/browser/android/vr/.+",
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+            ".+/[+]/device/vr/android/.+",
+            ".+/[+]/third_party/gvr-android-sdk/.+",
+            ".+/[+]/third_party/arcore-android-sdk/.+",
+            ".+/[+]/third_party/arcore-android-sdk-client/.+",
+        ],
+    ),
+)
+
+# TODO(crbug/1182468) Remove when experiment is done.
+try_.builder(
+    name = "android-pie-arm64-coverage-experimental-rel",
+    builderless = True,
+    cores = 16,
+    goma_jobs = goma.jobs.J300,
+    ssd = True,
+    main_list_view = "try",
+    use_clang_coverage = True,
+    tryjob = try_.job(
+        experiment_percentage = 3,
+    ),
+)
+
+try_.orchestrator_pair_builders(
+    name = "android-pie-arm64-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    orchestrator_cores = 4,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 32,
+    compilator_goma_jobs = goma.jobs.J300,
+    compilator_name = "android-pie-arm64-rel-compilator",
+)
+
+try_.builder(
+    name = "android-pie-arm64-rel-rts",
+    builderless = not settings.is_main,
+    cores = 16,
+    goma_jobs = goma.jobs.J300,
+    ssd = True,
+    main_list_view = "try",
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+    # TODO(crbug/1202741)
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "android-pie-x86-rel",
+    goma_jobs = goma.jobs.J150,
+)
+
+# TODO(crbug/1182468) Remove when coverage is enabled on CQ.
+try_.builder(
+    name = "android-pie-arm64-coverage-rel",
+    cores = 16,
+    goma_jobs = goma.jobs.J300,
+    ssd = True,
+    use_clang_coverage = True,
+)
+
+try_.builder(
+    name = "android-10-x86-fyi-rel-tests",
+)
+
+try_.builder(
+    name = "android-pie-arm64-wpt-rel-non-cq",
+)
+
+try_.builder(
+    name = "android-web-platform-pie-x86-fyi-rel",
+)
+
+try_.builder(
+    name = "android-weblayer-10-x86-rel-tests",
+)
+
+try_.builder(
+    name = "android-weblayer-marshmallow-x86-rel-tests",
+)
+
+try_.builder(
+    name = "android-weblayer-pie-x86-rel-tests",
+)
+
+try_.builder(
+    name = "android-weblayer-pie-x86-wpt-fyi-rel",
+)
+
+try_.builder(
+    name = "android-weblayer-pie-x86-wpt-smoketest",
+)
+
+try_.builder(
+    name = "android-webview-pie-x86-wpt-fyi-rel",
+)
+
+try_.builder(
+    name = "android-webview-marshmallow-arm64-dbg",
+)
+
+try_.builder(
+    name = "android-webview-nougat-arm64-dbg",
+)
+
+try_.builder(
+    name = "android-webview-oreo-arm64-dbg",
+)
+
+try_.builder(
+    name = "android-webview-pie-arm64-dbg",
+)
+
+try_.builder(
+    name = "android-webview-pie-arm64-fyi-rel",
+)
+
+try_.builder(
+    name = "android_archive_rel_ng",
+)
+
+try_.builder(
+    name = "android_arm64_dbg_recipe",
+    goma_jobs = goma.jobs.J300,
+)
+
+try_.builder(
+    name = "android_blink_rel",
+)
+
+try_.builder(
+    name = "android_cfi_rel_ng",
+    cores = 32,
+)
+
+try_.builder(
+    name = "android_clang_dbg_recipe",
+    goma_jobs = goma.jobs.J300,
+)
+
+try_.builder(
+    name = "android_compile_dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    goma_jobs = goma.jobs.J150,
+    main_list_view = "try",
+    tryjob = try_.job(),
+    # TODO(crbug/1202741)
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "android_compile_x64_dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    cores = 16,
+    ssd = True,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+",
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+            ".+/[+]/sandbox/linux/seccomp-bpf/.+",
+            ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+",
+            ".+/[+]/sandbox/linux/system_headers/.+",
+            ".+/[+]/sandbox/linux/tests/.+",
+            ".+/[+]/third_party/gvr-android-sdk/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "android_compile_x86_dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    cores = 16,
+    ssd = True,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/android/java/src/org/chromium/chrome/browser/vr/.+",
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+            ".+/[+]/sandbox/linux/seccomp-bpf/.+",
+            ".+/[+]/sandbox/linux/seccomp-bpf-helpers/.+",
+            ".+/[+]/sandbox/linux/system_headers/.+",
+            ".+/[+]/sandbox/linux/tests/.+",
+            ".+/[+]/third_party/gvr-android-sdk/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "android_cronet",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+    # TODO(crbug/1202741)
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "android_mojo",
+)
+
+try_.builder(
+    name = "android_n5x_swarming_dbg",
+)
+
+try_.builder(
+    name = "android_unswarmed_pixel_aosp",
+)
+
+try_.builder(
+    name = "cast_shell_android",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+    # TODO(crbug/1202741)
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "linux_android_dbg_ng",
+)
+
+try_.builder(
+    name = "try-nougat-phone-tester",
+)
+
+try_.gpu.optional_tests_builder(
+    name = "android_optional_gpu_tests_rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    goma_jobs = goma.jobs.J150,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/cc/.+",
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+            ".+/[+]/components/viz/.+",
+            ".+/[+]/content/test/gpu/.+",
+            ".+/[+]/gpu/.+",
+            ".+/[+]/media/audio/.+",
+            ".+/[+]/media/base/.+",
+            ".+/[+]/media/capture/.+",
+            ".+/[+]/media/filters/.+",
+            ".+/[+]/media/gpu/.+",
+            ".+/[+]/media/mojo/.+",
+            ".+/[+]/media/renderers/.+",
+            ".+/[+]/media/video/.+",
+            ".+/[+]/services/viz/.+",
+            ".+/[+]/testing/trigger_scripts/.+",
+            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
+            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/.+",
+        ],
+    ),
+)
+
+# RTS builders
+
+try_.builder(
+    name = "android-marshmallow-x86-rel-rts",
+    goma_jobs = goma.jobs.J300,
+    builderless = False,
+    cores = 16,
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+    ssd = True,
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star b/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star
new file mode 100644
index 0000000..9783efcc
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.angle.star
@@ -0,0 +1,94 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.angle builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/consoles.star", "consoles")
+load("//lib/try.star", "try_")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.angle",
+    builderless = False,
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    goma_jobs = goma.jobs.J150,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.gpu.SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.angle",
+)
+
+try_.builder(
+    name = "android-angle-chromium-try",
+    os = os.LINUX_BIONIC_REMOVE,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "android-angle-try",
+    os = os.LINUX_BIONIC_REMOVE,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "android_angle_rel_ng",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "fuchsia-angle-rel",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "fuchsia-angle-try",
+    os = os.LINUX_BIONIC_REMOVE,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "linux-angle-rel",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "linux-angle-chromium-try",
+    os = os.LINUX_BIONIC_REMOVE,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "linux-angle-try",
+    os = os.LINUX_BIONIC_REMOVE,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "mac-angle-chromium-try",
+    cores = None,
+    os = os.MAC_ANY,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "win-angle-chromium-x64-try",
+    os = os.WINDOWS_ANY,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "win-angle-chromium-x86-try",
+    os = os.WINDOWS_ANY,
+    executable = "recipe:angle_chromium_trybot",
+)
+
+try_.builder(
+    name = "win-angle-x64-try",
+    os = os.WINDOWS_ANY,
+    executable = "recipe:angle_chromium_trybot",
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
new file mode 100644
index 0000000..c86f82d
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.chromiumos.star
@@ -0,0 +1,208 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.chromiumos builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+load("//project.star", "settings")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.chromiumos",
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.chromiumos",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+)
+
+try_.builder(
+    name = "chromeos-amd64-generic-cfi-thin-lto-rel",
+)
+
+try_.builder(
+    name = "chromeos-amd64-generic-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/content/gpu/.+",
+            ".+/[+]/media/.+",
+        ],
+    ),
+)
+
+try_.orchestrator_pair_builders(
+    name = "chromeos-amd64-generic-rel",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    main_list_view = "try",
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 16,
+    compilator_name = "chromeos-amd64-generic-rel-compilator",
+)
+
+try_.builder(
+    name = "chromeos-arm-generic-dbg",
+)
+
+try_.builder(
+    name = "chromeos-arm-generic-rel",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    bootstrap = True,
+    mirrors = ["ci/chromeos-arm-generic-rel"],
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    os = os.LINUX_BIONIC_REMOVE,
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "lacros-amd64-generic-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "lacros-arm-generic-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "linux-chromeos-compile-dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    os = os.LINUX_BIONIC_REMOVE,
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "chromeos-kevin-compile-rel",
+)
+
+try_.builder(
+    name = "chromeos-kevin-rel",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/build/chromeos/.+",
+            ".+/[+]/build/config/chromeos/.*",
+            ".+/[+]/chromeos/CHROMEOS_LKGM",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "linux-chromeos-inverse-fieldtrials-fyi-rel",
+)
+
+try_.orchestrator_pair_builders(
+    name = "linux-chromeos-rel",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+    main_list_view = "try",
+    use_clang_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 32,
+    compilator_goma_jobs = goma.jobs.J300,
+    compilator_name = "linux-chromeos-rel-compilator",
+)
+
+try_.builder(
+    name = "linux-chromeos-js-code-coverage",
+    use_clang_coverage = True,
+    use_javascript_coverage = True,
+)
+
+try_.builder(
+    name = "linux-lacros-dbg",
+)
+
+try_.builder(
+    name = "linux-lacros-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    cores = 16,
+    ssd = True,
+    goma_jobs = goma.jobs.J300,
+    main_list_view = "try",
+    tryjob = try_.job(),
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "linux-lacros-rel-code-coverage",
+    cores = 16,
+    ssd = True,
+    goma_jobs = goma.jobs.J300,
+    main_list_view = "try",
+    use_clang_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    os = os.LINUX_BIONIC_REMOVE,
+    tryjob = try_.job(
+        experiment_percentage = 3,
+    ),
+)
+
+try_.builder(
+    name = "linux-lacros-rel-rts",
+    builderless = False,
+    cores = 16,
+    ssd = True,
+    goma_jobs = goma.jobs.J300,
+    main_list_view = "try",
+    os = os.LINUX_BIONIC_REMOVE,
+    tryjob = try_.job(
+        experiment_percentage = 1,
+    ),
+)
+
+try_.builder(
+    name = "linux-chromeos-dbg",
+)
+
+try_.builder(
+    name = "linux-chromeos-annotator-rel",
+)
+
+try_.builder(
+    name = "linux-cfm-rel",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chromeos/components/chromebox_for_meetings/.+",
+            ".+/[+]/chromeos/dbus/chromebox_for_meetings/.+",
+            ".+/[+]/chromeos/services/chromebox_for_meetings/.+",
+            ".+/[+]/chrome/browser/chromeos/chromebox_for_meetings/.+",
+            ".+/[+]/chrome/browser/resources/chromeos/chromebox_for_meetings/.+",
+            ".+/[+]/chrome/browser/ui/webui/chromeos/chromebox_for_meetings/.+",
+            ".+/[+]/chrome/test/data/webui/chromeos/chromebox_for_meetings/.+",
+        ],
+    ),
+)
+
+# RTS builders
+
+try_.builder(
+    name = "chromeos-amd64-generic-rel-rts",
+    builderless = False,
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
new file mode 100644
index 0000000..fc16452
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.dawn.star
@@ -0,0 +1,147 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.swangle builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os")
+load("//lib/consoles.star", "consoles")
+load("//lib/try.star", "try_")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.dawn",
+    builderless = False,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.gpu.SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.dawn",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+)
+
+try_.builder(
+    name = "dawn-linux-x64-deps-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    os = os.LINUX_BIONIC_REMOVE,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/gpu/.+",
+            ".+/[+]/testing/buildbot/chromium.dawn.json",
+            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
+            ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/features.gni",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "dawn-mac-x64-deps-rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    main_list_view = "try",
+    os = os.MAC_ANY,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/gpu/.+",
+            ".+/[+]/testing/buildbot/chromium.dawn.json",
+            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
+            ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/features.gni",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "dawn-win10-x64-deps-rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    main_list_view = "try",
+    os = os.WINDOWS_ANY,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/gpu/.+",
+            ".+/[+]/testing/buildbot/chromium.dawn.json",
+            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
+            ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/features.gni",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "dawn-win10-x86-deps-rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    main_list_view = "try",
+    os = os.WINDOWS_ANY,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/gpu/.+",
+            ".+/[+]/testing/buildbot/chromium.dawn.json",
+            ".+/[+]/third_party/blink/renderer/modules/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/external/wpt/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/wpt_internal/webgpu/.+",
+            ".+/[+]/third_party/blink/web_tests/WebGPUExpectations",
+            ".+/[+]/third_party/dawn/.+",
+            ".+/[+]/third_party/webgpu-cts/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/features.gni",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "linux-dawn-rel",
+    os = os.LINUX_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "mac-dawn-rel",
+    os = os.MAC_ANY,
+)
+
+try_.builder(
+    name = "dawn-try-mac-amd-exp",
+    builderless = True,
+    os = os.MAC_ANY,
+    pool = "luci.chromium.gpu.mac.retina.amd.try",
+)
+
+try_.builder(
+    name = "dawn-try-mac-intel-exp",
+    builderless = True,
+    os = os.MAC_ANY,
+    pool = "luci.chromium.gpu.mac.mini.intel.uhd630.try",
+)
+
+try_.builder(
+    name = "win-dawn-rel",
+    os = os.WINDOWS_ANY,
+)
+
+try_.builder(
+    name = "dawn-try-win10-x86-rel",
+    os = os.WINDOWS_ANY,
+)
+
+try_.builder(
+    name = "dawn-try-win10-x64-asan-rel",
+    os = os.WINDOWS_ANY,
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
new file mode 100644
index 0000000..dfaf996
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.linux.star
@@ -0,0 +1,626 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.linux builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+load("//project.star", "settings")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.linux",
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.linux",
+    branch_selector = branches.CROS_LTS_MILESTONE,
+)
+
+try_.builder(
+    name = "cast_shell_audio_linux",
+)
+
+try_.builder(
+    name = "cast_shell_linux",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "cast_shell_linux_dbg",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chromecast/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "cast_shell_linux_arm64",
+    branch_selector = branches.MAIN,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chromecast/.+",
+        ],
+    ),
+    os = os.LINUX_BIONIC,
+)
+
+try_.builder(
+    name = "cast-binary-size",
+    builderless = True,
+    executable = "recipe:binary_size_cast_trybot",
+    properties = {
+        "$build/binary_size": {
+            "analyze_targets": [
+                "//chromecast:cast_shell",
+            ],
+            "compile_targets": [
+                "cast_shell",
+            ],
+        },
+    },
+)
+
+try_.builder(
+    name = "fuchsia-binary-size",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = True,
+    executable = "recipe:binary_size_fuchsia_trybot",
+    properties = {
+        "$build/binary_size": {
+            "analyze_targets": [
+                "//fuchsia/release:fuchsia_sizes",
+            ],
+            "compile_targets": [
+                "fuchsia_sizes",
+            ],
+        },
+    },
+    tryjob = try_.job(
+        experiment_percentage = 20,
+    ),
+)
+
+try_.builder(
+    name = "fuchsia-arm64-cast",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chromecast/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "fuchsia-compile-x64-dbg",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/base/fuchsia/.+",
+            ".+/[+]/fuchsia/.+",
+            ".+/[+]/media/fuchsia/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "fuchsia-deterministic-dbg",
+    executable = "recipe:swarming/deterministic_build",
+)
+
+try_.builder(
+    name = "fuchsia-fyi-arm64-dbg",
+)
+
+try_.builder(
+    name = "fuchsia-fyi-arm64-femu",
+)
+
+try_.builder(
+    name = "fuchsia-fyi-arm64-rel",
+)
+
+try_.builder(
+    name = "fuchsia-fyi-x64-dbg",
+)
+
+try_.builder(
+    name = "fuchsia-fyi-x64-rel",
+)
+
+try_.builder(
+    name = "fuchsia-x64-cast",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "fuchsia_arm64",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "fuchsia_x64",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "layout_test_leak_detection",
+)
+
+try_.builder(
+    name = "leak_detection_linux",
+)
+
+try_.builder(
+    name = "linux-1mbu-compile-fyi-rel",
+    builderless = False,
+    goma_jobs = goma.jobs.J150,
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+    properties = {
+        "bot_update_experiments": [
+            "no_sync",
+        ],
+    },
+)
+
+try_.builder(
+    name = "linux-annotator-rel",
+)
+
+try_.builder(
+    name = "linux-autofill-assistant",
+)
+
+try_.builder(
+    name = "linux-bfcache-rel",
+)
+
+try_.builder(
+    name = "linux-bionic-rel",
+    goma_jobs = goma.jobs.J150,
+    os = os.LINUX_BIONIC,
+)
+
+try_.builder(
+    name = "linux-blink-heap-concurrent-marking-tsan-rel",
+)
+
+try_.builder(
+    name = "linux-blink-heap-verification-try",
+)
+
+try_.builder(
+    name = "linux-blink-v8-oilpan",
+)
+
+try_.builder(
+    name = "linux-blink-web-tests-force-accessibility-rel",
+)
+
+try_.builder(
+    name = "linux-clang-tidy-dbg",
+    executable = "recipe:tricium_clang_tidy_wrapper",
+    goma_jobs = goma.jobs.J150,
+)
+
+try_.builder(
+    name = "linux-clang-tidy-rel",
+    executable = "recipe:tricium_clang_tidy_wrapper",
+    goma_jobs = goma.jobs.J150,
+)
+
+try_.builder(
+    name = "linux-dcheck-off-rel",
+)
+
+try_.builder(
+    name = "linux-example-builder",
+)
+
+try_.builder(
+    name = "linux-extended-tracing-rel",
+)
+
+try_.builder(
+    name = "linux-gcc-rel",
+    goma_backend = None,
+)
+
+try_.builder(
+    name = "linux-headless-shell-rel",
+)
+
+try_.builder(
+    name = "linux-inverse-fieldtrials-fyi-rel",
+)
+
+try_.builder(
+    name = "linux-mbi-mode-per-render-process-host-rel",
+)
+
+try_.builder(
+    name = "linux-mbi-mode-per-site-instance-rel",
+)
+
+try_.builder(
+    name = "linux-lacros-fyi-rel",
+)
+
+try_.builder(
+    name = "linux-lacros-version-skew-fyi",
+)
+
+try_.builder(
+    name = "linux-layout-tests-edit-ng",
+)
+
+try_.builder(
+    name = "linux-libfuzzer-asan-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    executable = "recipe:chromium_libfuzzer_trybot",
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "linux-perfetto-rel",
+    tryjob = try_.job(
+        experiment_percentage = 100,
+        location_regexp = [
+            ".+/[+]/base/trace_event/.+",
+            ".+/[+]/base/tracing/.+",
+            ".+/[+]/components/tracing/.+",
+            ".+/[+]/content/browser/tracing/.+",
+            ".+/[+]/services/tracing/.+",
+        ],
+    ),
+)
+
+try_.orchestrator_pair_builders(
+    name = "linux-rel",
+    bootstrap = True,
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    use_clang_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 16,
+    compilator_goma_jobs = goma.jobs.J150,
+    compilator_name = "linux-rel-compilator",
+)
+
+try_.builder(
+    name = "linux-wayland-rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "linux-trusty-rel",
+    goma_jobs = goma.jobs.J150,
+    os = os.LINUX_TRUSTY,
+)
+
+try_.builder(
+    name = "linux-xenial-rel",
+    goma_jobs = goma.jobs.J150,
+    os = os.LINUX_XENIAL,
+)
+
+try_.builder(
+    name = "linux-viz-rel",
+)
+
+try_.builder(
+    name = "linux-webkit-msan-rel",
+)
+
+try_.builder(
+    name = "linux-wpt-fyi-rel",
+)
+
+try_.builder(
+    name = "linux-wpt-identity-fyi-rel",
+)
+
+try_.builder(
+    name = "linux-wpt-input-fyi-rel",
+)
+
+try_.builder(
+    name = "linux_chromium_analysis",
+)
+
+try_.builder(
+    name = "linux_chromium_archive_rel_ng",
+)
+
+try_.orchestrator_pair_builders(
+    name = "linux_chromium_asan_rel_ng",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 16,
+    compilator_goma_jobs = goma.jobs.J150,
+    compilator_name = "linux_chromium_asan_rel_ng-compilator",
+)
+
+try_.builder(
+    name = "linux_chromium_asan_rel_ng_rts",
+    goma_jobs = goma.jobs.J150,
+    ssd = True,
+    main_list_view = "try",
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+)
+
+try_.builder(
+    name = "linux_chromium_cfi_rel_ng",
+    cores = 32,
+    # TODO(thakis): Remove once https://crbug.com/927738 is resolved.
+    execution_timeout = 7 * time.hour,
+)
+
+try_.builder(
+    name = "linux_chromium_chromeos_asan_rel_ng",
+    goma_jobs = goma.jobs.J150,
+    # TODO(crbug/1144484): Remove this timeout once we figure out the
+    # regression in compiler or toolchain.
+    execution_timeout = 7 * time.hour,
+)
+
+try_.builder(
+    name = "linux_chromium_chromeos_msan_rel_ng",
+    goma_jobs = goma.jobs.J150,
+)
+
+try_.builder(
+    name = "linux_chromium_clobber_deterministic",
+    executable = "recipe:swarming/deterministic_build",
+    execution_timeout = 6 * time.hour,
+)
+
+try_.builder(
+    name = "linux_chromium_clobber_rel_ng",
+)
+
+try_.builder(
+    name = "linux_chromium_compile_dbg_32_ng",
+)
+
+try_.builder(
+    name = "linux_chromium_compile_dbg_ng",
+    branch_selector = branches.STANDARD_MILESTONE,
+    builderless = not settings.is_main,
+    caches = [
+        swarming.cache(
+            name = "builder",
+            path = "linux_debug",
+        ),
+    ],
+    goma_jobs = goma.jobs.J150,
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "linux_chromium_compile_rel_ng",
+)
+
+try_.builder(
+    name = "linux_chromium_dbg_ng",
+    branch_selector = branches.STANDARD_MILESTONE,
+    caches = [
+        swarming.cache(
+            name = "builder",
+            path = "linux_debug",
+        ),
+    ],
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/build/.*check_gn_headers.*",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "linux_chromium_msan_rel_ng",
+    execution_timeout = 6 * time.hour,
+    goma_jobs = goma.jobs.J150,
+)
+
+try_.orchestrator_pair_builders(
+    name = "linux_chromium_tsan_rel_ng",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 16,
+    compilator_goma_jobs = goma.jobs.J150,
+    compilator_name = "linux_chromium_tsan_rel_ng-compilator",
+)
+
+try_.builder(
+    name = "linux_chromium_tsan_rel_ng_rts",
+    builderless = not settings.is_main,
+    goma_jobs = goma.jobs.J150,
+    main_list_view = "try",
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+)
+
+try_.builder(
+    name = "linux_chromium_ubsan_rel_ng",
+)
+
+try_.builder(
+    name = "linux_layout_tests_layout_ng_disabled",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/third_party/blink/renderer/core/editing/.+",
+            ".+/[+]/third_party/blink/renderer/core/layout/.+",
+            ".+/[+]/third_party/blink/renderer/core/paint/.+",
+            ".+/[+]/third_party/blink/renderer/core/svg/.+",
+            ".+/[+]/third_party/blink/renderer/platform/fonts/shaping/.+",
+            ".+/[+]/third_party/blink/renderer/platform/graphics/.+",
+            ".+/[+]/third_party/blink/web_tests/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "linux_mojo",
+)
+
+try_.builder(
+    name = "linux_mojo_chromeos",
+)
+
+try_.builder(
+    name = "linux_upload_clang",
+    builderless = True,
+    cores = 32,
+    executable = "recipe:chromium_upload_clang",
+    goma_backend = None,
+    os = os.LINUX_TRUSTY,
+)
+
+try_.builder(
+    name = "linux_vr",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "network_service_linux",
+)
+
+try_.builder(
+    name = "tricium-metrics-analysis",
+    executable = "recipe:tricium_metrics",
+)
+
+try_.builder(
+    name = "tricium-oilpan-analysis",
+    executable = "recipe:tricium_oilpan",
+)
+
+try_.builder(
+    name = "tricium-simple",
+    executable = "recipe:tricium_simple",
+)
+
+try_.gpu.optional_tests_builder(
+    name = "linux_optional_gpu_tests_rel",
+    branch_selector = branches.STANDARD_MILESTONE,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+            ".+/[+]/content/test/gpu/.+",
+            ".+/[+]/gpu/.+",
+            ".+/[+]/media/audio/.+",
+            ".+/[+]/media/base/.+",
+            ".+/[+]/media/capture/.+",
+            ".+/[+]/media/filters/.+",
+            ".+/[+]/media/gpu/.+",
+            ".+/[+]/media/mojo/.+",
+            ".+/[+]/media/renderers/.+",
+            ".+/[+]/media/video/.+",
+            ".+/[+]/testing/buildbot/chromium.gpu.fyi.json",
+            ".+/[+]/testing/trigger_scripts/.+",
+            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
+            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/.+",
+        ],
+    ),
+)
+
+# Stable testing builders
+
+try_.builder(
+    name = "linux-stable-filter-rel",
+    builderless = False,
+    goma_jobs = goma.jobs.J150,
+    use_clang_coverage = True,
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "linux-stable-filter-combined-rel",
+    builderless = False,
+    goma_jobs = goma.jobs.J150,
+    use_clang_coverage = True,
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+# RTS builders (https://crbug.com/1203048)
+
+try_.builder(
+    name = "linux-rel-rts",
+    builderless = False,
+    goma_jobs = goma.jobs.J150,
+    use_clang_coverage = True,
+    tryjob = try_.job(
+        experiment_percentage = 5,
+    ),
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
+
+try_.builder(
+    name = "fuchsia_x64_rts",
+    builderless = False,
+    os = os.LINUX_XENIAL_OR_BIONIC_REMOVE,
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
new file mode 100644
index 0000000..b617bbb
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.mac.star
@@ -0,0 +1,290 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.mac builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "cpu", "goma", "os", "xcode")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+load("//project.star", "settings")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.mac",
+    builderless = True,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.MAC_ANY,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+    ssd = True,
+)
+
+def ios_builder(*, name, **kwargs):
+    kwargs.setdefault("builderless", False)
+    kwargs.setdefault("os", os.MAC_11)
+    kwargs.setdefault("ssd", None)
+    kwargs.setdefault("xcode", xcode.x13main)
+    return try_.builder(name = name, **kwargs)
+
+consoles.list_view(
+    name = "tryserver.chromium.mac",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+)
+
+try_.builder(
+    name = "mac-osxbeta-rel",
+    os = os.MAC_DEFAULT,
+)
+
+try_.builder(
+    name = "mac-inverse-fieldtrials-fyi-rel",
+    os = os.MAC_DEFAULT,
+)
+
+try_.builder(
+    name = "mac-rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    builderless = not settings.is_main,
+    use_clang_coverage = True,
+    goma_jobs = goma.jobs.J150,
+    main_list_view = "try",
+    os = os.MAC_DEFAULT,
+    tryjob = try_.job(),
+)
+
+try_.orchestrator_pair_builders(
+    name = "mac-rel-orchestrator",
+    main_list_view = "try",
+    use_clang_coverage = True,
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(
+        experiment_percentage = 1,
+    ),
+    compilator_goma_jobs = goma.jobs.J150,
+    os = os.MAC_DEFAULT,
+    compilator_name = "mac-rel-compilator",
+)
+
+try_.orchestrator_pair_builders(
+    name = "mac11-arm64-rel",
+    main_list_view = "try",
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(
+        experiment_percentage = 50,
+    ),
+    compilator_goma_jobs = goma.jobs.J150,
+    os = os.MAC_11,
+    compilator_name = "mac11-arm64-rel-compilator",
+    # TODO (crbug/1271287): Revert when root issue is fixed
+    compilator_grace_period = 4 * time.minute,
+)
+
+# NOTE: the following trybots aren't sensitive to Mac version on which
+# they are built, hence no additional dimension is specified.
+# The 10.xx version translates to which bots will run isolated tests.
+try_.builder(
+    name = "mac_chromium_10.11_rel_ng",
+)
+
+try_.builder(
+    name = "mac_chromium_10.12_rel_ng",
+)
+
+try_.builder(
+    name = "mac_chromium_10.13_rel_ng",
+)
+
+try_.builder(
+    name = "mac_chromium_10.14_rel_ng",
+)
+
+try_.builder(
+    name = "mac_chromium_10.15_rel_ng",
+)
+
+try_.builder(
+    name = "mac_chromium_11.0_rel_ng",
+    builderless = False,
+)
+
+try_.builder(
+    name = "mac_chromium_archive_rel_ng",
+)
+
+try_.builder(
+    name = "mac_chromium_asan_rel_ng",
+    goma_jobs = goma.jobs.J150,
+)
+
+try_.builder(
+    name = "mac_chromium_compile_dbg_ng",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    goma_jobs = goma.jobs.J150,
+    os = os.MAC_DEFAULT,
+    main_list_view = "try",
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "mac_chromium_compile_rel_ng",
+)
+
+try_.builder(
+    name = "mac_chromium_dbg_ng",
+)
+
+try_.builder(
+    name = "mac_upload_clang",
+    builderless = False,
+    executable = "recipe:chromium_upload_clang",
+    execution_timeout = 6 * time.hour,
+    goma_backend = None,  # Does not use Goma.
+)
+
+try_.builder(
+    name = "mac_upload_clang_arm",
+    builderless = False,
+    executable = "recipe:chromium_upload_clang",
+    execution_timeout = 6 * time.hour,
+    goma_backend = None,  # Does not use Goma.
+)
+
+ios_builder(
+    name = "ios-catalyst",
+)
+
+ios_builder(
+    name = "ios-device",
+)
+
+ios_builder(
+    name = "ios-simulator",
+    branch_selector = branches.STANDARD_MILESTONE,
+    check_for_flakiness = True,
+    main_list_view = "try",
+    use_clang_coverage = True,
+    coverage_exclude_sources = "ios_test_files_and_test_utils",
+    coverage_test_types = ["overall", "unit"],
+    tryjob = try_.job(),
+)
+
+ios_builder(
+    name = "ios-simulator-cronet",
+    branch_selector = branches.STANDARD_MILESTONE,
+    check_for_flakiness = True,
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/components/cronet/.+",
+            ".+/[+]/components/grpc_support/.+",
+            ".+/[+]/ios/.+",
+        ],
+        location_regexp_exclude = [
+            ".+/[+]/components/cronet/android/.+",
+        ],
+    ),
+)
+
+ios_builder(
+    name = "ios-simulator-full-configs",
+    branch_selector = branches.STANDARD_MILESTONE,
+    check_for_flakiness = True,
+    main_list_view = "try",
+    use_clang_coverage = True,
+    coverage_exclude_sources = "ios_test_files_and_test_utils",
+    coverage_test_types = ["overall", "unit"],
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/ios/.+",
+        ],
+    ),
+)
+
+ios_builder(
+    name = "ios-simulator-inverse-fieldtrials-fyi",
+)
+
+ios_builder(
+    name = "ios-simulator-multi-window",
+)
+
+ios_builder(
+    name = "ios-simulator-noncq",
+    xcode = xcode.x13main,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/third_party/crashpad/crashpad/.+",
+        ],
+    ),
+)
+
+ios_builder(
+    name = "ios14-beta-simulator",
+    os = os.MAC_11,
+)
+
+ios_builder(
+    name = "ios14-sdk-simulator",
+    os = os.MAC_11,
+    cpu = cpu.ARM64,
+)
+
+ios_builder(
+    name = "ios15-beta-simulator",
+)
+
+ios_builder(
+    name = "ios15-sdk-simulator",
+    xcode = xcode.x13latestbeta,
+)
+
+try_.gpu.optional_tests_builder(
+    name = "mac_optional_gpu_tests_rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    main_list_view = "try",
+    ssd = None,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+            ".+/[+]/content/test/gpu/.+",
+            ".+/[+]/gpu/.+",
+            ".+/[+]/media/audio/.+",
+            ".+/[+]/media/base/.+",
+            ".+/[+]/media/capture/.+",
+            ".+/[+]/media/filters/.+",
+            ".+/[+]/media/gpu/.+",
+            ".+/[+]/media/mojo/.+",
+            ".+/[+]/media/renderers/.+",
+            ".+/[+]/media/video/.+",
+            ".+/[+]/services/shape_detection/.+",
+            ".+/[+]/testing/buildbot/chromium.gpu.fyi.json",
+            ".+/[+]/testing/trigger_scripts/.+",
+            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
+            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/.+",
+        ],
+    ),
+)
+
+# RTS builders
+
+try_.builder(
+    name = "mac-rel-rts",
+    builderless = False,
+    goma_jobs = goma.jobs.J150,
+    use_clang_coverage = True,
+)
+
+ios_builder(
+    name = "ios-simulator-rts",
+    builderless = False,
+    coverage_exclude_sources = "ios_test_files_and_test_utils",
+    coverage_test_types = ["unit"],
+    use_clang_coverage = True,
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.packager.star b/infra/config/subprojects/chromium/try/tryserver.chromium.packager.star
new file mode 100644
index 0000000..a7cd025
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.packager.star
@@ -0,0 +1,63 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.packager builder group."""
+
+load("//lib/builders.star", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.packager",
+    executable = "recipe:chromium_3pp",
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    pool = try_.DEFAULT_POOL,
+    service_account = "chromium-cipd-try-builder@chops-service-accounts.iam.gserviceaccount.com",
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.packager",
+)
+
+try_.builder(
+    name = "3pp-linux-amd64-packager",
+    builderless = False,
+    cores = 8,
+    os = os.LINUX_XENIAL_OR_BIONIC_SWITCH_TO_DEFAULT,
+    properties = {
+        "$build/chromium_3pp": {
+            "platform": "linux-amd64",
+            "package_prefix": "chromium_3pp",
+            "preprocess": [{
+                "name": "third_party/android_deps",
+                "cmd": [
+                    "{CHECKOUT}/src/third_party/android_deps/fetch_all.py",
+                    "-v",
+                    "--ignore-vulnerabilities",
+                ],
+            }],
+            "gclient_config": "chromium",
+            "gclient_apply_config": ["android"],
+        },
+    },
+    tryjob = try_.job(
+        location_regexp = [
+            # Enable for CLs touching files under "3pp" directories which are
+            # two level deep or more from the repo root.
+            ".+/[+]/.+/3pp/.+",
+        ],
+    ),
+)
+
+try_.builder(
+    name = "3pp-mac-amd64-packager",
+    builderless = True,
+    os = os.MAC_DEFAULT,
+    properties = {
+        "$build/chromium_3pp": {
+            "platform": "mac-amd64",
+            "package_prefix": "chromium_3pp",
+            "gclient_config": "chromium",
+        },
+    },
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.rust.star b/infra/config/subprojects/chromium/try/tryserver.chromium.rust.star
new file mode 100644
index 0000000..adb55e0
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.rust.star
@@ -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.
+"""Definitions of builders in the tryserver.chromium.rust builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.rust",
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_DEFAULT,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.rust",
+)
+
+try_.builder(
+    name = "linux-rust-x64-rel",
+)
+
+try_.builder(
+    name = "android-rust-arm-rel",
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.star b/infra/config/subprojects/chromium/try/tryserver.chromium.star
new file mode 100644
index 0000000..d08e7d20
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.star
@@ -0,0 +1,63 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium",
+    builderless = True,
+    cores = 32,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+)
+
+try_.builder(
+    name = "android-official",
+    branch_selector = branches.STANDARD_MILESTONE,
+)
+
+try_.builder(
+    name = "fuchsia-official",
+    branch_selector = branches.STANDARD_MILESTONE,
+)
+
+try_.builder(
+    name = "linux-official",
+    branch_selector = branches.STANDARD_MILESTONE,
+)
+
+try_.builder(
+    name = "mac-official",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    cores = None,
+    execution_timeout = 30 * time.hour,
+    os = os.MAC_ANY,
+)
+
+try_.builder(
+    name = "win-official",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    os = os.WINDOWS_DEFAULT,
+    execution_timeout = 6 * time.hour,
+)
+
+try_.builder(
+    name = "win32-official",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    os = os.WINDOWS_DEFAULT,
+    execution_timeout = 6 * time.hour,
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.updater.star b/infra/config/subprojects/chromium/try/tryserver.chromium.updater.star
new file mode 100644
index 0000000..ebdb981
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.updater.star
@@ -0,0 +1,71 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.updater builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.updater",
+    builderless = True,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.updater",
+)
+
+def updater_mac_builder(*, name, **kwargs):
+    kwargs.setdefault("os", os.MAC_ANY)
+    return try_.builder(name = name, **kwargs)
+
+def updater_windows_builder(*, name, **kwargs):
+    kwargs.setdefault("cores", 8)
+    kwargs.setdefault("os", os.WINDOWS_DEFAULT)
+    return try_.builder(name = name, **kwargs)
+
+updater_mac_builder(
+    name = "mac-updater-try-builder-dbg",
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/updater/.+",
+        ],
+    ),
+)
+
+updater_mac_builder(
+    name = "mac-updater-try-builder-rel",
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/updater/.+",
+        ],
+    ),
+)
+
+updater_windows_builder(
+    name = "win-updater-try-builder-dbg",
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/updater/.+",
+        ],
+    ),
+)
+
+updater_windows_builder(
+    name = "win-updater-try-builder-rel",
+    main_list_view = "try",
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/updater/.+",
+        ],
+    ),
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.chromium.win.star b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
new file mode 100644
index 0000000..56e15f6
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.chromium.win.star
@@ -0,0 +1,201 @@
+# 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.
+"""Definitions of builders in the tryserver.chromium.win builder group."""
+
+load("//lib/branches.star", "branches")
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+
+try_.defaults.set(
+    builder_group = "tryserver.chromium.win",
+    builderless = True,
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    os = os.WINDOWS_DEFAULT,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.chromium.win",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+)
+
+try_.builder(
+    name = "win-annotator-rel",
+)
+
+try_.builder(
+    name = "win-asan",
+    goma_jobs = goma.jobs.J150,
+)
+
+try_.builder(
+    name = "win-celab-try-rel",
+    executable = "recipe:celab",
+    properties = {
+        "exclude": "chrome_only",
+        "pool_name": "celab-chromium-try",
+        "pool_size": 20,
+        "tests": "*",
+    },
+)
+
+try_.builder(
+    name = "win-libfuzzer-asan-rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    builderless = False,
+    executable = "recipe:chromium_libfuzzer_trybot",
+    main_list_view = "try",
+    os = os.WINDOWS_ANY,
+    tryjob = try_.job(),
+)
+
+try_.builder(
+    name = "win_archive",
+)
+
+try_.builder(
+    name = "win_chromium_compile_dbg_ng",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    goma_jobs = goma.jobs.J150,
+    main_list_view = "try",
+    tryjob = try_.job(),
+    builderless = False,
+    cores = 16,
+    ssd = True,
+)
+
+try_.builder(
+    name = "win_chromium_compile_rel_ng",
+)
+
+try_.builder(
+    name = "win_chromium_dbg_ng",
+)
+
+try_.builder(
+    name = "win_chromium_x64_rel_ng",
+)
+
+try_.builder(
+    name = "win_mojo",
+)
+
+try_.builder(
+    name = "win_upload_clang",
+    builderless = False,
+    cores = 32,
+    executable = "recipe:chromium_upload_clang",
+    goma_backend = None,
+    os = os.WINDOWS_ANY,
+    execution_timeout = 6 * time.hour,
+)
+
+try_.builder(
+    name = "win_x64_archive",
+)
+
+try_.builder(
+    name = "win10_chromium_x64_20h2_fyi_rel_ng",
+    builderless = False,
+    use_clang_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    os = os.WINDOWS_10_20h2,
+)
+
+try_.builder(
+    name = "win10_chromium_x64_dbg_ng",
+    os = os.WINDOWS_10,
+)
+
+try_.builder(
+    name = "win10_chromium_inverse_fieldtrials_x64_fyi_rel_ng",
+    os = os.WINDOWS_10,
+)
+
+try_.orchestrator_pair_builders(
+    name = "win10_chromium_x64_rel_ng",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    use_clang_coverage = True,
+    coverage_test_types = ["unit", "overall"],
+    main_list_view = "try",
+    orchestrator_cores = 2,
+    orchestrator_tryjob = try_.job(),
+    compilator_cores = 32,
+    compilator_goma_jobs = goma.jobs.J300,
+    compilator_name = "win10_chromium_x64_rel_ng-compilator",
+    # TODO (crbug/1271287): Revert when root issue is fixed
+    compilator_grace_period = 3 * time.minute,
+)
+
+try_.builder(
+    name = "win10_chromium_x64_rel_ng_exp",
+    builderless = False,
+    os = os.WINDOWS_ANY,
+)
+
+try_.builder(
+    name = "win7-rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    cores = 16,
+    execution_timeout = 4 * time.hour + 30 * time.minute,
+    goma_jobs = goma.jobs.J300,
+    main_list_view = "try",
+    ssd = True,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/sandbox/win/.+",
+            ".+/[+]/sandbox/policy/win/.+",
+        ],
+    ),
+)
+
+try_.gpu.optional_tests_builder(
+    name = "win_optional_gpu_tests_rel",
+    branch_selector = branches.DESKTOP_EXTENDED_STABLE_MILESTONE,
+    builderless = True,
+    main_list_view = "try",
+    os = os.WINDOWS_DEFAULT,
+    tryjob = try_.job(
+        location_regexp = [
+            ".+/[+]/chrome/browser/vr/.+",
+            ".+/[+]/content/browser/xr/.+",
+            ".+/[+]/content/test/gpu/.+",
+            ".+/[+]/device/vr/.+",
+            ".+/[+]/gpu/.+",
+            ".+/[+]/media/audio/.+",
+            ".+/[+]/media/base/.+",
+            ".+/[+]/media/capture/.+",
+            ".+/[+]/media/filters/.+",
+            ".+/[+]/media/gpu/.+",
+            ".+/[+]/media/mojo/.+",
+            ".+/[+]/media/renderers/.+",
+            ".+/[+]/media/video/.+",
+            ".+/[+]/testing/buildbot/chromium.gpu.fyi.json",
+            ".+/[+]/testing/trigger_scripts/.+",
+            ".+/[+]/third_party/blink/renderer/modules/vr/.+",
+            ".+/[+]/third_party/blink/renderer/modules/mediastream/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webcodecs/.+",
+            ".+/[+]/third_party/blink/renderer/modules/webgl/.+",
+            ".+/[+]/third_party/blink/renderer/modules/xr/.+",
+            ".+/[+]/third_party/blink/renderer/platform/graphics/gpu/.+",
+            ".+/[+]/tools/clang/scripts/update.py",
+            ".+/[+]/ui/gl/.+",
+        ],
+    ),
+)
+
+# RTS builders
+
+try_.builder(
+    name = "win10_chromium_x64_rel_ng_rts",
+    goma_jobs = goma.jobs.J150,
+    use_clang_coverage = True,
+    builderless = False,
+    cores = 16,
+)
diff --git a/infra/config/subprojects/chromium/try/tryserver.infra.star b/infra/config/subprojects/chromium/try/tryserver.infra.star
new file mode 100644
index 0000000..2f24a27b
--- /dev/null
+++ b/infra/config/subprojects/chromium/try/tryserver.infra.star
@@ -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.
+"""Definitions of builders in the tryserver.infra builder group."""
+
+load("//lib/builders.star", "goma", "os")
+load("//lib/try.star", "try_")
+load("//lib/consoles.star", "consoles")
+
+try_.defaults.set(
+    builder_group = "tryserver.infra",
+    cores = 8,
+    executable = try_.DEFAULT_EXECUTABLE,
+    execution_timeout = try_.DEFAULT_EXECUTION_TIMEOUT,
+    goma_backend = goma.backend.RBE_PROD,
+    pool = try_.DEFAULT_POOL,
+    service_account = try_.DEFAULT_SERVICE_ACCOUNT,
+)
+
+consoles.list_view(
+    name = "tryserver.infra",
+)
+
+try_.builder(
+    name = "linux-bootstrap",
+    bootstrap = True,
+    mirrors = [
+        "ci/linux-bootstrap",
+        "ci/linux-bootstrap-tests",
+    ],
+    os = os.LINUX_BIONIC_SWITCH_TO_DEFAULT,
+)
+
+try_.builder(
+    name = "win-bootstrap",
+    bootstrap = True,
+    builderless = True,
+    os = os.WINDOWS_10,
+)
diff --git a/ios/chrome/browser/discover_feed/BUILD.gn b/ios/chrome/browser/discover_feed/BUILD.gn
index 89fd0117..fd6b463 100644
--- a/ios/chrome/browser/discover_feed/BUILD.gn
+++ b/ios/chrome/browser/discover_feed/BUILD.gn
@@ -18,7 +18,7 @@
     "//components/signin/public/identity_manager",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/signin",
-    "//ios/chrome/browser/ui/content_suggestions:metrics",
+    "//ios/chrome/browser/ui/ntp:metrics",
     "//ios/public/provider/chrome/browser",
     "//ios/public/provider/chrome/browser/discover_feed",
   ]
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.h b/ios/chrome/browser/discover_feed/discover_feed_service.h
index 04483a77..abe1ac92 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_service.h
+++ b/ios/chrome/browser/discover_feed/discover_feed_service.h
@@ -10,7 +10,7 @@
 #include "components/signin/public/identity_manager/identity_manager.h"
 
 class AuthenticationService;
-@class DiscoverFeedMetricsRecorder;
+@class FeedMetricsRecorder;
 class PrefService;
 
 // A browser-context keyed service that is used to keep the Discover Feed data
@@ -29,8 +29,8 @@
   ~DiscoverFeedService() override;
 
   // Returns the FeedMetricsRecorder to be used by the Feed, a single instance
-  // of DiscoverFeedMetricsRecorder needs to be used per BrowserState.
-  DiscoverFeedMetricsRecorder* GetDiscoverFeedMetricsRecorder();
+  // of FeedMetricsRecorder needs to be used per BrowserState.
+  FeedMetricsRecorder* GetFeedMetricsRecorder();
 
   // KeyedService:
   void Shutdown() override;
@@ -46,7 +46,7 @@
       identity_manager_observation_{this};
 
   // Metrics recorder for the DiscoverFeed.
-  __strong DiscoverFeedMetricsRecorder* discover_feed_metrics_recorder_ = nil;
+  __strong FeedMetricsRecorder* feed_metrics_recorder_ = nil;
 };
 
 #endif  // IOS_CHROME_BROWSER_DISCOVER_FEED_DISCOVER_FEED_SERVICE_H_
diff --git a/ios/chrome/browser/discover_feed/discover_feed_service.mm b/ios/chrome/browser/discover_feed/discover_feed_service.mm
index db3f30d..dc03f2eb 100644
--- a/ios/chrome/browser/discover_feed/discover_feed_service.mm
+++ b/ios/chrome/browser/discover_feed/discover_feed_service.mm
@@ -5,7 +5,7 @@
 #include "ios/chrome/browser/discover_feed/discover_feed_service.h"
 
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
+#import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
 #import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
 #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h"
 #import "ios/public/provider/chrome/browser/discover_feed/discover_feed_provider.h"
@@ -21,22 +21,21 @@
   if (identity_manager)
     identity_manager_observation_.Observe(identity_manager);
 
-  discover_feed_metrics_recorder_ = [[DiscoverFeedMetricsRecorder alloc] init];
+  feed_metrics_recorder_ = [[FeedMetricsRecorder alloc] init];
 
   DiscoverFeedConfiguration* discover_config =
       [[DiscoverFeedConfiguration alloc] init];
   discover_config.authService = authentication_service;
   discover_config.prefService = pref_service;
-  discover_config.metricsRecorder = discover_feed_metrics_recorder_;
+  discover_config.metricsRecorder = feed_metrics_recorder_;
   ios::GetChromeBrowserProvider().GetDiscoverFeedProvider()->StartFeed(
       discover_config);
 }
 
 DiscoverFeedService::~DiscoverFeedService() {}
 
-DiscoverFeedMetricsRecorder*
-DiscoverFeedService::GetDiscoverFeedMetricsRecorder() {
-  return discover_feed_metrics_recorder_;
+FeedMetricsRecorder* DiscoverFeedService::GetFeedMetricsRecorder() {
+  return feed_metrics_recorder_;
 }
 
 void DiscoverFeedService::Shutdown() {
@@ -45,7 +44,7 @@
   // Stop the Discover feed to disconnects its services.
   ios::GetChromeBrowserProvider().GetDiscoverFeedProvider()->StopFeed();
 
-  discover_feed_metrics_recorder_ = nil;
+  feed_metrics_recorder_ = nil;
 }
 
 void DiscoverFeedService::OnPrimaryAccountChanged(
diff --git a/ios/chrome/browser/ui/content_suggestions/BUILD.gn b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
index f679ab8..18eab060 100644
--- a/ios/chrome/browser/ui/content_suggestions/BUILD.gn
+++ b/ios/chrome/browser/ui/content_suggestions/BUILD.gn
@@ -24,7 +24,6 @@
   deps = [
     ":constants",
     ":feature_flags",
-    ":metrics",
     "//base",
     "//components/favicon/core",
     "//components/favicon/ios",
@@ -95,7 +94,6 @@
     "//ui/base",
     "//ui/strings",
   ]
-  public_deps = [ ":metrics" ]
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
@@ -141,7 +139,6 @@
     ":content_suggestions_constant",
     ":content_suggestions_ui_util",
     ":feature_flags",
-    ":metrics",
     "resources:content_suggestions_no_image",
     "resources:content_suggestions_offline",
     "resources:ntp_search_icon",
@@ -178,19 +175,6 @@
   configs += [ "//build/config/compiler:enable_arc" ]
 }
 
-source_set("metrics") {
-  sources = [
-    "discover_feed_metrics_recorder.h",
-    "discover_feed_metrics_recorder.mm",
-  ]
-  deps = [
-    ":feature_flags",
-    "//base",
-    "//components/feed/core/v2/public:common",
-  ]
-  configs += [ "//build/config/compiler:enable_arc" ]
-}
-
 source_set("content_suggestions_constant") {
   sources = [
     "ntp_home_constant.h",
diff --git a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
index d073501..0cd5e00 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.h
@@ -13,7 +13,7 @@
 
 @class ContentSuggestionsHeaderViewController;
 @protocol DiscoverFeedDelegate;
-@class DiscoverFeedMetricsRecorder;
+@class FeedMetricsRecorder;
 @protocol NewTabPageCommands;
 @protocol NewTabPageControllerDelegate;
 @class NTPHomeMediator;
@@ -51,9 +51,8 @@
 // Command handler for NTP related commands.
 @property(nonatomic, weak) id<NewTabPageCommands> ntpCommandHandler;
 
-// Metrics recorder for the Discover feed events related to ContentSuggestions.
-@property(nonatomic, strong)
-    DiscoverFeedMetricsRecorder* discoverFeedMetricsRecorder;
+// Metrics recorder for the feed events related to ContentSuggestions.
+@property(nonatomic, strong) FeedMetricsRecorder* feedMetricsRecorder;
 
 // Delegate used to communicate to communicate events to the DiscoverFeed.
 @property(nonatomic, weak) id<DiscoverFeedDelegate> discoverFeedDelegate;
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 87b682c..7eb2517 100644
--- a/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/content_suggestions_coordinator.mm
@@ -47,7 +47,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_menu_provider.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
-#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_metrics.h"
@@ -55,6 +54,7 @@
 #import "ios/chrome/browser/ui/main/scene_state_browser_agent.h"
 #import "ios/chrome/browser/ui/menu/browser_action_factory.h"
 #import "ios/chrome/browser/ui/menu/menu_histograms.h"
+#import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_commands.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
@@ -239,7 +239,7 @@
   self.ntpMediator.suggestionsViewController = self.suggestionsViewController;
   self.ntpMediator.suggestionsMediator = self.contentSuggestionsMediator;
   [self.ntpMediator setUp];
-  self.ntpMediator.discoverFeedMetrics = self.discoverFeedMetricsRecorder;
+  self.ntpMediator.feedMetricsRecorder = self.feedMetricsRecorder;
 
   [self.suggestionsViewController addChildViewController:self.headerController];
   [self.headerController
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h
index 92879a1..82e0d32 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h
@@ -32,7 +32,7 @@
 @class NewTabPageViewController;
 @protocol NTPHomeConsumer;
 @class NTPHomeMetrics;
-@class DiscoverFeedMetricsRecorder;
+@class FeedMetricsRecorder;
 @protocol OmniboxCommands;
 class TemplateURLService;
 @protocol SnackbarCommands;
@@ -65,8 +65,8 @@
         dispatcher;
 // Recorder for the metrics related to the NTP.
 @property(nonatomic, strong) NTPHomeMetrics* NTPMetrics;
-// Recorder for the metrics related to the Discover feed.
-@property(nonatomic, strong) DiscoverFeedMetricsRecorder* discoverFeedMetrics;
+// Recorder for the metrics related to the feed.
+@property(nonatomic, strong) FeedMetricsRecorder* feedMetricsRecorder;
 // View Controller for the NTP if using the non refactored NTP or the Feed is
 // not visible.
 // TODO(crbug.com/1114792): Create a protocol to avoid duplication and update
diff --git a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
index 12662ed..94e68b81 100644
--- a/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
+++ b/ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.mm
@@ -42,12 +42,12 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_mediator.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_view_controller_audience.h"
-#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_consumer.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_metrics.h"
 #import "ios/chrome/browser/ui/content_suggestions/user_account_image_update_delegate.h"
 #import "ios/chrome/browser/ui/default_promo/default_browser_utils.h"
 #import "ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.h"
+#import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
 #include "ios/chrome/browser/ui/ntp/metrics.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_header_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h"
@@ -366,17 +366,17 @@
 
 - (void)handleFeedManageActivityTapped {
   [self openMenuItemWebPage:GURL(kFeedManageActivityURL)];
-  [self.discoverFeedMetrics recordHeaderMenuManageActivityTapped];
+  [self.feedMetricsRecorder recordHeaderMenuManageActivityTapped];
 }
 
 - (void)handleFeedManageInterestsTapped {
   [self openMenuItemWebPage:GURL(kFeedManageInterestsURL)];
-  [self.discoverFeedMetrics recordHeaderMenuManageInterestsTapped];
+  [self.feedMetricsRecorder recordHeaderMenuManageInterestsTapped];
 }
 
 - (void)handleFeedLearnMoreTapped {
   [self openMenuItemWebPage:GURL(kFeedLearnMoreURL)];
-  [self.discoverFeedMetrics recordHeaderMenuLearnMoreTapped];
+  [self.feedMetricsRecorder recordHeaderMenuLearnMoreTapped];
 }
 
 - (void)openMostRecentTab:(CollectionViewItem*)item {
diff --git a/ios/chrome/browser/ui/first_run/first_run_egtest.mm b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
index 01923b7..4c64432d 100644
--- a/ios/chrome/browser/ui/first_run/first_run_egtest.mm
+++ b/ios/chrome/browser/ui/first_run/first_run_egtest.mm
@@ -776,7 +776,8 @@
 // Checks that it is possible to add an account even if there is already account
 // and that it is possible to switch accounts when multiple accounts are
 // present.
-- (void)testSignInSelectAccount {
+// TODO(crbug.com/1266372): Fix issue finding needed element.
+- (void)DISABLED_testSignInSelectAccount {
   FakeChromeIdentity* fakeIdentity1 = [SigninEarlGrey fakeIdentity1];
   FakeChromeIdentity* fakeIdentity2 = [SigninEarlGrey fakeIdentity2];
   [SigninEarlGrey addFakeIdentity:fakeIdentity1];
@@ -901,7 +902,8 @@
 
 // Checks that sync is turned on after the user chose to turn on
 // sync in the advanced sync settings screen.
-- (void)testCustomSyncOn {
+// TODO(crbug.com/1266372): Fix issue finding needed element.
+- (void)DISABLED_testCustomSyncOn {
   FakeChromeIdentity* fakeIdentity = [SigninEarlGrey fakeIdentity1];
   [SigninEarlGrey addFakeIdentity:fakeIdentity];
 
diff --git a/ios/chrome/browser/ui/ntp/BUILD.gn b/ios/chrome/browser/ui/ntp/BUILD.gn
index 0a93ce8..7bc8633e 100644
--- a/ios/chrome/browser/ui/ntp/BUILD.gn
+++ b/ios/chrome/browser/ui/ntp/BUILD.gn
@@ -15,6 +15,7 @@
   public_deps = [
     ":constants",
     ":feature_flags",
+    ":metrics",
   ]
   deps = [ "//ios/chrome/browser/ui/util" ]
 }
@@ -126,6 +127,7 @@
   ]
   deps = [
     ":logo",
+    ":metrics",
     ":ntp",
     "resources:incognito_icon",
     "resources:ntp_opentabs",
@@ -169,7 +171,6 @@
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_constant",
     "//ios/chrome/browser/ui/content_suggestions:content_suggestions_ui",
-    "//ios/chrome/browser/ui/content_suggestions:metrics",
     "//ios/chrome/browser/ui/content_suggestions/cells",
     "//ios/chrome/browser/ui/favicon",
     "//ios/chrome/browser/ui/gestures",
@@ -205,6 +206,18 @@
   ]
 }
 
+source_set("metrics") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "feed_metrics_recorder.h",
+    "feed_metrics_recorder.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/feed/core/v2/public:common",
+  ]
+}
+
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
diff --git a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.h
similarity index 92%
rename from ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
rename to ios/chrome/browser/ui/ntp/feed_metrics_recorder.h
index 72dec5e..c2cb532 100644
--- a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h
+++ b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_DISCOVER_FEED_METRICS_RECORDER_H_
-#define IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_DISCOVER_FEED_METRICS_RECORDER_H_
+#ifndef IOS_CHROME_BROWSER_UI_NTP_FEED_METRICS_RECORDER_H_
+#define IOS_CHROME_BROWSER_UI_NTP_FEED_METRICS_RECORDER_H_
 
 #import <UIKit/UIKit.h>
 
@@ -21,9 +21,8 @@
   kMaxValue = 4,
 };
 
-// Records different metrics for the NTP's Discover feed.
-// TODO(crbug.com/1200303): Move this file to */ui/ntp.
-@interface DiscoverFeedMetricsRecorder : NSObject
+// Records different metrics for the NTP feeds.
+@interface FeedMetricsRecorder : NSObject
 
 // Record metrics for when the user has scrolled |scrollDistance| in the Feed.
 - (void)recordFeedScrolled:(int)scrollDistance;
@@ -148,4 +147,4 @@
 
 @end
 
-#endif  // IOS_CHROME_BROWSER_UI_CONTENT_SUGGESTIONS_DISCOVER_FEED_METRICS_RECORDER_H_
+#endif  // IOS_CHROME_BROWSER_UI_NTP_FEED_METRICS_RECORDER_H_
diff --git a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
similarity index 98%
rename from ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
rename to ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
index 8f15b408..7dd4c16 100644
--- a/ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.mm
+++ b/ios/chrome/browser/ui/ntp/feed_metrics_recorder.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
+#import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
 
 #import "base/mac/foundation_util.h"
 #import "base/metrics/histogram_functions.h"
@@ -10,7 +10,6 @@
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
 #import "components/feed/core/v2/public/common_enums.h"
-#import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
 #error "This file requires ARC support."
@@ -195,7 +194,7 @@
 const int kMinutesBetweenSessions = 5;
 }  // namespace
 
-@interface DiscoverFeedMetricsRecorder ()
+@interface FeedMetricsRecorder ()
 
 // Tracking property to avoid duplicate recordings of
 // FeedEngagementType::kFeedEngagedSimple.
@@ -211,12 +210,11 @@
 
 @end
 
-@implementation DiscoverFeedMetricsRecorder
+@implementation FeedMetricsRecorder
 
 #pragma mark - Public
 
 - (void)recordFeedScrolled:(int)scrollDistance {
-  DCHECK(IsDiscoverFeedEnabled());
   [self recordEngagement:scrollDistance interacted:NO];
 
   if (!self.scrolledReported) {
@@ -510,7 +508,6 @@
 
 // Records any direct interaction with the Feed, this doesn't include scrolling.
 - (void)recordInteraction {
-  DCHECK(IsDiscoverFeedEnabled());
   [self recordEngagement:0 interacted:YES];
   [self recordEngagementTypeHistogram:FeedEngagementType::kFeedInteracted];
 }
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
index 13c4084..1799d3ea 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_coordinator.mm
@@ -36,7 +36,6 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_feature.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizer.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.h"
-#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_mediator.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_metrics.h"
 #import "ios/chrome/browser/ui/context_menu/link_preview/link_preview_coordinator.h"
@@ -49,6 +48,7 @@
 #import "ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.h"
 #import "ios/chrome/browser/ui/ntp/feed_header_view_controller.h"
 #import "ios/chrome/browser/ui/ntp/feed_menu_commands.h"
+#import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/ntp/incognito_view_controller.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_commands.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_content_delegate.h"
@@ -182,8 +182,7 @@
 @property(nonatomic, assign) TemplateURLService* templateURLService;
 
 // Metrics recorder for actions relating to the feed.
-@property(nonatomic, strong)
-    DiscoverFeedMetricsRecorder* discoverFeedMetricsRecorder;
+@property(nonatomic, strong) FeedMetricsRecorder* feedMetricsRecorder;
 
 @end
 
@@ -255,8 +254,7 @@
       DiscoverFeedServiceFactory::GetForBrowserState(
           self.browser->GetBrowserState());
 
-  self.discoverFeedMetricsRecorder =
-      discoverFeedService->GetDiscoverFeedMetricsRecorder();
+  self.feedMetricsRecorder = discoverFeedService->GetFeedMetricsRecorder();
 
   self.contentSuggestionsCoordinator =
       [self createContentSuggestionsCoordinator];
@@ -323,7 +321,7 @@
       ->RemoveFeedViewController(self.discoverFeedViewController);
   self.discoverFeedWrapperViewController = nil;
   self.discoverFeedViewController = nil;
-  self.discoverFeedMetricsRecorder = nil;
+  self.feedMetricsRecorder = nil;
 
   [self.containedViewController willMoveToParentViewController:nil];
   [self.containedViewController.view removeFromSuperview];
@@ -402,8 +400,7 @@
       self.feedHeaderViewController;
 
   [self configureMainViewControllerUsing:self.ntpViewController];
-  self.ntpViewController.discoverFeedMetricsRecorder =
-      self.discoverFeedMetricsRecorder;
+  self.ntpViewController.feedMetricsRecorder = self.feedMetricsRecorder;
   self.ntpViewController.bubblePresenter = self.bubblePresenter;
 }
 
@@ -840,8 +837,7 @@
 // menu. A hidden feed will continue to show the header, with a modified label.
 - (void)setFeedVisibleFromHeader:(BOOL)visible {
   [self.feedExpandedPref setValue:visible];
-  [self.discoverFeedMetricsRecorder
-      recordDiscoverFeedVisibilityChanged:visible];
+  [self.feedMetricsRecorder recordDiscoverFeedVisibilityChanged:visible];
 }
 
 // Configures and returns the NTP mediator.
@@ -879,8 +875,7 @@
   contentSuggestionsCoordinator.ntpMediator = self.ntpMediator;
   contentSuggestionsCoordinator.ntpCommandHandler = self;
   contentSuggestionsCoordinator.discoverFeedDelegate = self;
-  contentSuggestionsCoordinator.discoverFeedMetricsRecorder =
-      self.discoverFeedMetricsRecorder;
+  contentSuggestionsCoordinator.feedMetricsRecorder = self.feedMetricsRecorder;
   [contentSuggestionsCoordinator start];
   contentSuggestionsCoordinator.headerController.baseViewController =
       self.baseViewController;
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
index eb4117c..616dad22 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.h
@@ -13,7 +13,7 @@
 @class BubblePresenter;
 @class ContentSuggestionsHeaderViewController;
 @class ContentSuggestionsViewController;
-@class DiscoverFeedMetricsRecorder;
+@class FeedMetricsRecorder;
 @class DiscoverFeedWrapperViewController;
 @class FeedHeaderViewController;
 @protocol FeedMenuCommands;
@@ -58,9 +58,8 @@
 @property(nonatomic, strong)
     UICollectionViewController* contentSuggestionsViewController;
 
-// Discover Feed metrics recorder.
-@property(nonatomic, strong)
-    DiscoverFeedMetricsRecorder* discoverFeedMetricsRecorder;
+// Feed metrics recorder.
+@property(nonatomic, strong) FeedMetricsRecorder* feedMetricsRecorder;
 
 // Whether or not the feed is visible.
 @property(nonatomic, assign, getter=isFeedVisible) BOOL feedVisible;
diff --git a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
index e20cf58..ede3292 100644
--- a/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
+++ b/ios/chrome/browser/ui/ntp/new_tab_page_view_controller.mm
@@ -11,12 +11,12 @@
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_synchronizing.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_header_view_controller.h"
 #import "ios/chrome/browser/ui/content_suggestions/content_suggestions_layout.h"
-#import "ios/chrome/browser/ui/content_suggestions/discover_feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/content_suggestions/ntp_home_constant.h"
 #import "ios/chrome/browser/ui/gestures/view_revealing_vertical_pan_handler.h"
 #import "ios/chrome/browser/ui/ntp/discover_feed_wrapper_view_controller.h"
 #import "ios/chrome/browser/ui/ntp/feed_header_view_controller.h"
 #import "ios/chrome/browser/ui/ntp/feed_menu_commands.h"
+#import "ios/chrome/browser/ui/ntp/feed_metrics_recorder.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_constants.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_content_delegate.h"
 #import "ios/chrome/browser/ui/ntp/new_tab_page_feature.h"
@@ -135,7 +135,7 @@
     [self.contentSuggestionsViewController willMoveToParentViewController:nil];
     [self.contentSuggestionsViewController.view removeFromSuperview];
     [self.contentSuggestionsViewController removeFromParentViewController];
-    [self.discoverFeedMetricsRecorder
+    [self.feedMetricsRecorder
         recordBrokenNTPHierarchy:BrokenNTPHierarchyRelationship::
                                      kContentSuggestionsReset];
   }
@@ -452,7 +452,7 @@
                                               willDecelerate:decelerate];
   [self.panGestureHandler scrollViewDidEndDragging:scrollView
                                     willDecelerate:decelerate];
-  [self.discoverFeedMetricsRecorder
+  [self.feedMetricsRecorder
       recordFeedScrolled:scrollView.contentOffset.y - self.scrollStartPosition];
 }
 
@@ -739,7 +739,7 @@
 // Handles device rotation.
 - (void)deviceOrientationDidChange {
   if (self.viewDidAppear) {
-    [self.discoverFeedMetricsRecorder
+    [self.feedMetricsRecorder
         recordDeviceOrientationChanged:[[UIDevice currentDevice] orientation]];
   }
 }
@@ -851,7 +851,7 @@
         didMoveToParentViewController:self.discoverFeedWrapperViewController
                                           .discoverFeed];
 
-    [self.discoverFeedMetricsRecorder
+    [self.feedMetricsRecorder
         recordBrokenNTPHierarchy:BrokenNTPHierarchyRelationship::
                                      kContentSuggestionsParent];
   }
@@ -879,7 +879,7 @@
     DCHECK([parentView.subviews containsObject:subView]);
     [subView removeFromSuperview];
     [parentView addSubview:subView];
-    [self.discoverFeedMetricsRecorder recordBrokenNTPHierarchy:relationship];
+    [self.feedMetricsRecorder recordBrokenNTPHierarchy:relationship];
   }
 }
 
diff --git a/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h b/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h
index 9c5f24f2..70a3547 100644
--- a/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h
+++ b/ios/public/provider/chrome/browser/discover_feed/discover_feed_configuration.h
@@ -8,10 +8,11 @@
 #import <Foundation/Foundation.h>
 
 class AuthenticationService;
-@class DiscoverFeedMetricsRecorder;
+@class FeedMetricsRecorder;
 class PrefService;
 
 // Configuration object used by the DiscoverFeedProvider.
+// TODO(crbug.com/1277504): Rename this to FeedConfiguration.
 @interface DiscoverFeedConfiguration : NSObject
 
 // AuthenticationService used by DiscoverFeedProvider.
@@ -20,8 +21,8 @@
 // PrefService used by DiscoverFeedProvider.
 @property(nonatomic, assign) PrefService* prefService;
 
-// DiscoverFeed metrics recorder used by DiscoverFeedProvider.
-@property(nonatomic, strong) DiscoverFeedMetricsRecorder* metricsRecorder;
+// Feed metrics recorder used by DiscoverFeedProvider.
+@property(nonatomic, strong) FeedMetricsRecorder* metricsRecorder;
 
 @end
 
diff --git a/net/BUILD.gn b/net/BUILD.gn
index f415d1b2..64ce731 100644
--- a/net/BUILD.gn
+++ b/net/BUILD.gn
@@ -2654,8 +2654,6 @@
     "tools/quic/quic_simple_server_session_helper.h",
     "tools/quic/quic_simple_server_socket.cc",
     "tools/quic/quic_simple_server_socket.h",
-    "tools/quic/quic_transport_simple_server.cc",
-    "tools/quic/quic_transport_simple_server.h",
     "tools/quic/synchronous_host_resolver.cc",
     "tools/quic/synchronous_host_resolver.h",
   ]
@@ -2705,17 +2703,6 @@
     allow_circular_includes_from =
         [ "//net/third_party/quiche:quic_server_core" ]
   }
-  executable("quic_transport_simple_server") {
-    sources = [ "tools/quic/quic_transport_simple_server_bin.cc" ]
-    deps = [
-      ":net",
-      ":simple_quic_tools",
-      "//base",
-      "//build/win:default_exe_manifest",
-      "//third_party/boringssl",
-      "//third_party/protobuf:protobuf_lite",
-    ]
-  }
   executable("crypto_message_printer") {
     sources = [ "tools/quic/crypto_message_printer_bin.cc" ]
     deps = [
diff --git a/net/tools/quic/quic_transport_simple_server.cc b/net/tools/quic/quic_transport_simple_server.cc
deleted file mode 100644
index aa3de30..0000000
--- a/net/tools/quic/quic_transport_simple_server.cc
+++ /dev/null
@@ -1,145 +0,0 @@
-// Copyright (c) 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.
-
-#include "net/tools/quic/quic_transport_simple_server.h"
-
-#include <stdlib.h>
-
-#include "base/run_loop.h"
-#include "base/threading/thread_task_runner_handle.h"
-#include "net/base/net_errors.h"
-#include "net/quic/address_utils.h"
-#include "net/quic/platform/impl/quic_chromium_clock.h"
-#include "net/quic/quic_chromium_alarm_factory.h"
-#include "net/quic/quic_chromium_connection_helper.h"
-#include "net/socket/udp_server_socket.h"
-#include "net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.h"
-#include "net/tools/quic/quic_simple_server_packet_writer.h"
-#include "net/tools/quic/quic_simple_server_socket.h"
-
-namespace net {
-namespace {
-
-using quic::CryptoHandshakeMessage;
-using quic::ParsedQuicVersion;
-using quic::QuicChromiumClock;
-using quic::QuicCryptoServerStreamBase;
-using quic::QuicSocketAddress;
-using quic::QuicTransportSimpleServerSession;
-
-constexpr char kSourceAddressTokenSecret[] = "test";
-constexpr size_t kMaxReadsPerEvent = 32;
-constexpr size_t kMaxNewConnectionsPerEvent = 32;
-constexpr int kReadBufferSize = 2 * quic::kMaxIncomingPacketSize;
-
-// TODO(vasilvv): move this into the shared code.
-quic::ParsedQuicVersionVector AllVersionsValidForQuicTransport() {
-  quic::ParsedQuicVersionVector result;
-  for (quic::ParsedQuicVersion version : quic::AllSupportedVersions()) {
-    if (!quic::IsVersionValidForQuicTransport(version))
-      continue;
-    result.push_back(version);
-  }
-  return result;
-}
-
-}  // namespace
-
-class QuicTransportSimpleServerSessionHelper
-    : public QuicCryptoServerStreamBase::Helper {
- public:
-  bool CanAcceptClientHello(const CryptoHandshakeMessage& /*message*/,
-                            const QuicSocketAddress& /*client_address*/,
-                            const QuicSocketAddress& /*peer_address*/,
-                            const QuicSocketAddress& /*self_address*/,
-                            std::string* /*error_details*/) const override {
-    return true;
-  }
-};
-
-QuicTransportSimpleServer::QuicTransportSimpleServer(
-    uint16_t port,
-    std::vector<url::Origin> accepted_origins,
-    std::unique_ptr<quic::ProofSource> proof_source)
-    : port_(port),
-      version_manager_(AllVersionsValidForQuicTransport()),
-      clock_(QuicChromiumClock::GetInstance()),
-      crypto_config_(kSourceAddressTokenSecret,
-                     quic::QuicRandom::GetInstance(),
-                     std::move(proof_source),
-                     quic::KeyExchangeSource::Default()),
-      dispatcher_(&config_,
-                  &crypto_config_,
-                  &version_manager_,
-                  std::make_unique<QuicChromiumConnectionHelper>(
-                      clock_,
-                      quic::QuicRandom::GetInstance()),
-                  std::make_unique<QuicTransportSimpleServerSessionHelper>(),
-                  std::make_unique<QuicChromiumAlarmFactory>(
-                      base::ThreadTaskRunnerHandle::Get().get(),
-                      clock_),
-                  quic::kQuicDefaultConnectionIdLength,
-                  accepted_origins),
-      read_buffer_(base::MakeRefCounted<IOBufferWithSize>(kReadBufferSize)) {}
-
-QuicTransportSimpleServer::~QuicTransportSimpleServer() {}
-
-int QuicTransportSimpleServer::Start() {
-  socket_ = CreateQuicSimpleServerSocket(
-      IPEndPoint{IPAddress::IPv6AllZeros(), port_}, &server_address_);
-  if (socket_ == nullptr)
-    return EXIT_FAILURE;
-
-  dispatcher_.InitializeWithWriter(
-      new QuicSimpleServerPacketWriter(socket_.get(), &dispatcher_));
-
-  ScheduleReadPackets();
-  return EXIT_SUCCESS;
-}
-
-void QuicTransportSimpleServer::ScheduleReadPackets() {
-  base::ThreadTaskRunnerHandle::Get()->PostTask(
-      FROM_HERE, base::BindOnce(&QuicTransportSimpleServer::ReadPackets,
-                                weak_factory_.GetWeakPtr()));
-}
-
-void QuicTransportSimpleServer::ReadPackets() {
-  dispatcher_.ProcessBufferedChlos(kMaxNewConnectionsPerEvent);
-  for (size_t i = 0; i < kMaxReadsPerEvent; i++) {
-    int result = socket_->RecvFrom(
-        read_buffer_.get(), read_buffer_->size(), &client_address_,
-        base::BindOnce(&QuicTransportSimpleServer::OnReadComplete,
-                       base::Unretained(this)));
-    if (result == ERR_IO_PENDING)
-      return;
-    ProcessReadPacket(result);
-  }
-  ScheduleReadPackets();
-}
-
-void QuicTransportSimpleServer::OnReadComplete(int result) {
-  ProcessReadPacket(result);
-  ReadPackets();
-}
-
-void QuicTransportSimpleServer::ProcessReadPacket(int result) {
-  if (result == 0)
-    result = ERR_CONNECTION_CLOSED;
-  if (result < 0) {
-    LOG(ERROR) << "QuicTransportSimpleServer read failed: "
-               << ErrorToString(result);
-    dispatcher_.Shutdown();
-    if (read_error_callback_) {
-      std::move(read_error_callback_).Run(result);
-    }
-    return;
-  }
-
-  quic::QuicReceivedPacket packet(read_buffer_->data(), /*length=*/result,
-                                  clock_->Now(), /*owns_buffer=*/false);
-  dispatcher_.ProcessPacket(ToQuicSocketAddress(server_address_),
-                            ToQuicSocketAddress(client_address_), packet);
-}
-
-}  // namespace net
diff --git a/net/tools/quic/quic_transport_simple_server.h b/net/tools/quic/quic_transport_simple_server.h
deleted file mode 100644
index df7481b..0000000
--- a/net/tools/quic/quic_transport_simple_server.h
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (c) 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 NET_TOOLS_QUIC_QUIC_TRANSPORT_SIMPLE_SERVER_H_
-#define NET_TOOLS_QUIC_QUIC_TRANSPORT_SIMPLE_SERVER_H_
-
-#include "base/memory/weak_ptr.h"
-#include "net/base/io_buffer.h"
-#include "net/base/ip_endpoint.h"
-#include "net/quic/platform/impl/quic_chromium_clock.h"
-#include "net/socket/udp_server_socket.h"
-#include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_server_config.h"
-#include "net/third_party/quiche/src/quic/core/quic_config.h"
-#include "net/third_party/quiche/src/quic/core/quic_version_manager.h"
-#include "net/third_party/quiche/src/quic/tools/quic_transport_simple_server_dispatcher.h"
-#include "net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.h"
-#include "url/origin.h"
-
-namespace net {
-
-// Server for QuicTransportSimpleSession.  This class is responsible for
-// creating a UDP server socket, listening on it and passing the packets
-// received to the dispatcher.
-class QuicTransportSimpleServer {
- public:
-  using ReadErrorCallback = base::OnceCallback<void(int)>;
-
-  QuicTransportSimpleServer(uint16_t port,
-                            std::vector<url::Origin> accepted_origins,
-                            std::unique_ptr<quic::ProofSource> proof_source);
-  ~QuicTransportSimpleServer();
-
-  int Start();
-
-  IPEndPoint server_address() const { return server_address_; }
-
-  void set_read_error_callback(ReadErrorCallback callback) {
-    read_error_callback_ = std::move(callback);
-  }
-
- private:
-  // Schedules a ReadPackets() call on the next iteration of the event loop.
-  void ScheduleReadPackets();
-  // Reads a fixed number of packets and then reschedules itself.
-  void ReadPackets();
-  // Called when an asynchronous read from the socket is complete.
-  void OnReadComplete(int result);
-  // Passes the most recently read packet into the dispatcher.
-  void ProcessReadPacket(int result);
-
-  const uint16_t port_;
-
-  ReadErrorCallback read_error_callback_;
-
-  quic::QuicVersionManager version_manager_;
-  quic::QuicChromiumClock* clock_;  // Not owned.
-  quic::QuicConfig config_;
-  quic::QuicCryptoServerConfig crypto_config_;
-
-  quic::QuicTransportSimpleServerDispatcher dispatcher_;
-  std::unique_ptr<UDPServerSocket> socket_;
-  IPEndPoint server_address_;
-
-  // Results of the potentially asynchronous read operation.
-  scoped_refptr<IOBufferWithSize> read_buffer_;
-  IPEndPoint client_address_;
-
-  base::WeakPtrFactory<QuicTransportSimpleServer> weak_factory_{this};
-};
-
-}  // namespace net
-
-#endif  // NET_TOOLS_QUIC_QUIC_TRANSPORT_SIMPLE_SERVER_H_
diff --git a/net/tools/quic/quic_transport_simple_server_bin.cc b/net/tools/quic/quic_transport_simple_server_bin.cc
deleted file mode 100644
index 74e602a..0000000
--- a/net/tools/quic/quic_transport_simple_server_bin.cc
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright (c) 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.
-
-#include "base/strings/string_split.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_default_proof_providers.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
-#include "net/third_party/quiche/src/quic/platform/api/quic_system_event_loop.h"
-#include "net/tools/quic/quic_transport_simple_server.h"
-#include "url/gurl.h"
-
-DEFINE_QUIC_COMMAND_LINE_FLAG(uint16_t, port, 20557, "The port to listen on.");
-
-DEFINE_QUIC_COMMAND_LINE_FLAG(std::string,
-                              accepted_origins,
-                              "",
-                              "Comma-separated list of accepted origins");
-
-int main(int argc, char** argv) {
-  const char* usage = "quic_transport_simple_server";
-  QuicSystemEventLoop event_loop("quic_transport_simple_server");
-  std::vector<std::string> non_option_args =
-      quic::QuicParseCommandLineFlags(usage, argc, argv);
-  if (!non_option_args.empty()) {
-    quic::QuicPrintCommandLineFlagHelp(usage);
-    return 0;
-  }
-
-  std::string accepted_origins_text = GetQuicFlag(FLAGS_accepted_origins);
-  std::vector<url::Origin> accepted_origins;
-  for (const base::StringPiece& origin :
-       base::SplitStringPiece(accepted_origins_text, ",", base::TRIM_WHITESPACE,
-                              base::SPLIT_WANT_NONEMPTY)) {
-    GURL url{origin};
-    if (!url.is_valid()) {
-      LOG(ERROR) << "Failed to parse origin specified: " << origin;
-      return 1;
-    }
-    accepted_origins.push_back(url::Origin::Create(url));
-  }
-
-  net::QuicTransportSimpleServer server(GetQuicFlag(FLAGS_port),
-                                        accepted_origins,
-                                        quic::CreateDefaultProofSource());
-  server.set_read_error_callback(
-      base::BindOnce([](int /*result*/) { exit(EXIT_FAILURE); }));
-  if (server.Start() != EXIT_SUCCESS)
-    return EXIT_FAILURE;
-
-  base::RunLoop run_loop;
-  run_loop.Run();
-  return EXIT_SUCCESS;
-}
diff --git a/services/network/web_transport.cc b/services/network/web_transport.cc
index fcb487f..cb1d93a8 100644
--- a/services/network/web_transport.cc
+++ b/services/network/web_transport.cc
@@ -17,7 +17,6 @@
 #include "net/third_party/quiche/src/quic/core/quic_time.h"
 #include "net/third_party/quiche/src/quic/core/quic_types.h"
 #include "net/third_party/quiche/src/quic/platform/api/quic_mem_slice.h"
-#include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h"
 #include "services/network/network_context.h"
 #include "services/network/public/mojom/web_transport.mojom.h"
 
diff --git a/services/network/web_transport_unittest.cc b/services/network/web_transport_unittest.cc
index efa20126..a521642 100644
--- a/services/network/web_transport_unittest.cc
+++ b/services/network/web_transport_unittest.cc
@@ -22,7 +22,6 @@
 #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h"
 #include "net/third_party/quiche/src/quic/test_tools/quic_test_backend.h"
 #include "net/tools/quic/quic_simple_server.h"
-#include "net/tools/quic/quic_transport_simple_server.h"
 #include "net/url_request/url_request_context.h"
 #include "services/network/network_context.h"
 #include "services/network/network_service.h"
@@ -263,7 +262,7 @@
 };
 
 quic::ParsedQuicVersion GetTestVersion() {
-  quic::ParsedQuicVersion version = quic::DefaultVersionForQuicTransport();
+  quic::ParsedQuicVersion version = quic::ParsedQuicVersion::RFCv1();
   quic::QuicEnableVersion(version);
   return version;
 }
diff --git a/services/tracing/perfetto/system_perfetto_unittest.cc b/services/tracing/perfetto/system_perfetto_unittest.cc
index 661931c1..b55b2e4 100644
--- a/services/tracing/perfetto/system_perfetto_unittest.cc
+++ b/services/tracing/perfetto/system_perfetto_unittest.cc
@@ -747,8 +747,9 @@
   PerfettoProducer::DeleteSoonForTesting(std::move(system_producer));
 }
 
-#if defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS) || defined(OS_ANDROID)
 // Flaky on all CrOS platforms: crbug.com/1262132#c18
+// Flaky on Android: crbug.com/1262132
 #define MAYBE_SystemTraceWhileLocalStartupTracing \
   DISABLED_SystemTraceWhileLocalStartupTracing
 #else
diff --git a/testing/buildbot/chromium.perf.json b/testing/buildbot/chromium.perf.json
index 3170bad..5df0696 100644
--- a/testing/buildbot/chromium.perf.json
+++ b/testing/buildbot/chromium.perf.json
@@ -88,7 +88,7 @@
           "ignore_task_failure": false,
           "io_timeout": 21600,
           "service_account": "chrome-tester@chops-service-accounts.iam.gserviceaccount.com",
-          "shards": 16
+          "shards": 10
         },
         "trigger_script": {
           "args": [
diff --git a/third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom b/third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom
index 9101ed1..db581287 100644
--- a/third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom
+++ b/third_party/blink/public/mojom/direct_sockets/direct_sockets.mojom
@@ -13,6 +13,18 @@
 import "services/network/public/mojom/tcp_socket.mojom";
 import "services/network/public/mojom/udp_socket.mojom";
 
+// This enum is used to track how often each permission check cause
+// Permission Denied failures. Used for histograms, don't renumber.
+enum DirectSocketFailureType {
+  kPermissionsPolicy = 0,
+  kTransientActivation = 1,
+  kUserDialog = 2,
+  kResolvingToNonPublic = 3,
+  kRateLimiting = 4,
+  kCORS = 5,
+  kEnterprisePolicy = 6
+};
+
 struct DirectSocketOptions {
   string? local_hostname;
   uint16 local_port = 0;
diff --git a/third_party/blink/public/mojom/webid/federated_auth_request.mojom b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
index 592699fa..d5d345b 100644
--- a/third_party/blink/public/mojom/webid/federated_auth_request.mojom
+++ b/third_party/blink/public/mojom/webid/federated_auth_request.mojom
@@ -14,13 +14,21 @@
   kSuccess,
   kApprovalDeclined,
   kErrorTooManyRequests,
-  kErrorFedCmNotSupportedByProvider,
-  kErrorFetchingWellKnown,
-  kErrorInvalidWellKnown,
+  kErrorFetchingWellKnownHttpNotFound,
+  kErrorFetchingWellKnownNoResponse,
+  kErrorFetchingWellKnownInvalidResponse,
+  kErrorFetchingClientIdMetadataHttpNotFound,
+  kErrorFetchingClientIdMetadataNoResponse,
+  kErrorFetchingClientIdMetadataInvalidResponse,
+  kErrorFetchingAccountsHttpNotFound,
+  kErrorFetchingAccountsNoResponse,
+  kErrorFetchingAccountsInvalidResponse,
+  kErrorFetchingIdTokenHttpNotFound,
+  kErrorFetchingIdTokenNoResponse,
+  kErrorFetchingIdTokenInvalidResponse,
+  kErrorFetchingIdTokenInvalidRequest,
   kErrorFetchingSignin,
   kErrorInvalidSigninResponse,
-  kErrorInvalidAccountsResponse,
-  kErrorInvalidTokenResponse,
   kErrorCanceled,
   kError,
 };
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
index 4222384..9f44b26 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.cc
@@ -51,6 +51,7 @@
 void LayoutNGBlockFlowMixin<Base>::StyleDidChange(
     StyleDifference diff,
     const ComputedStyle* old_style) {
+  Base::CheckIsNotDestroyed();
   Base::StyleDidChange(diff, old_style);
 
   if (diff.NeedsReshape()) {
@@ -58,24 +59,36 @@
   }
 }
 
+#if DCHECK_IS_ON()
+template <typename Base>
+void LayoutNGBlockFlowMixin<Base>::AddLayoutOverflowFromChildren() {
+  Base::CheckIsNotDestroyed();
+  NOTREACHED();
+}
+#endif
+
 template <typename Base>
 NGInlineNodeData* LayoutNGBlockFlowMixin<Base>::TakeNGInlineNodeData() {
+  Base::CheckIsNotDestroyed();
   return ng_inline_node_data_.Release();
 }
 
 template <typename Base>
 NGInlineNodeData* LayoutNGBlockFlowMixin<Base>::GetNGInlineNodeData() const {
+  Base::CheckIsNotDestroyed();
   DCHECK(ng_inline_node_data_);
   return ng_inline_node_data_;
 }
 
 template <typename Base>
 void LayoutNGBlockFlowMixin<Base>::ResetNGInlineNodeData() {
+  Base::CheckIsNotDestroyed();
   ng_inline_node_data_ = MakeGarbageCollected<NGInlineNodeData>();
 }
 
 template <typename Base>
 void LayoutNGBlockFlowMixin<Base>::ClearNGInlineNodeData() {
+  Base::CheckIsNotDestroyed();
   if (ng_inline_node_data_) {
     // ng_inline_node_data_ is not used from now on but exists until GC happens,
     // so it is better to eagerly clear HeapVector to improve memory
@@ -86,10 +99,18 @@
 }
 
 template <typename Base>
+bool LayoutNGBlockFlowMixin<Base>::HasNGInlineNodeData() const {
+  Base::CheckIsNotDestroyed();
+  return ng_inline_node_data_;
+}
+
+template <typename Base>
 void LayoutNGBlockFlowMixin<Base>::AddOutlineRects(
     Vector<PhysicalRect>& rects,
     const PhysicalOffset& additional_offset,
     NGOutlineType include_block_overflows) const {
+  Base::CheckIsNotDestroyed();
+
   // TODO(crbug.com/1145048): Currently |NGBoxPhysicalFragment| does not support
   // NG block fragmentation. Fallback to the legacy code path.
   if (Base::PhysicalFragmentCount() == 1) {
@@ -106,6 +127,8 @@
 
 template <typename Base>
 LayoutUnit LayoutNGBlockFlowMixin<Base>::FirstLineBoxBaseline() const {
+  Base::CheckIsNotDestroyed();
+
   if (const absl::optional<LayoutUnit> baseline =
           Base::FirstLineBoxBaselineOverride())
     return *baseline;
@@ -131,6 +154,8 @@
 template <typename Base>
 LayoutUnit LayoutNGBlockFlowMixin<Base>::InlineBlockBaseline(
     LineDirectionMode line_direction) const {
+  Base::CheckIsNotDestroyed();
+
   // Please see |LayoutNGMixin<Base>::Paint()| for these DCHECKs.
   DCHECK(Base::GetNGPaginationBreakability() ==
              LayoutNGBlockFlow::kForbidBreaks ||
@@ -169,6 +194,8 @@
     const HitTestLocation& hit_test_location,
     const PhysicalOffset& accumulated_offset,
     HitTestAction action) {
+  Base::CheckIsNotDestroyed();
+
   // Please see |LayoutNGMixin<Base>::Paint()| for these DCHECKs.
   DCHECK(Base::GetNGPaginationBreakability() ==
              LayoutNGBlockFlow::kForbidBreaks ||
@@ -199,6 +226,7 @@
 template <typename Base>
 PositionWithAffinity LayoutNGBlockFlowMixin<Base>::PositionForPoint(
     const PhysicalOffset& point) const {
+  Base::CheckIsNotDestroyed();
   DCHECK_GE(Base::GetDocument().Lifecycle().GetState(),
             DocumentLifecycle::kPrePaintClean);
 
@@ -222,6 +250,7 @@
 void LayoutNGBlockFlowMixin<Base>::DirtyLinesFromChangedChild(
     LayoutObject* child,
     MarkingBehavior marking_behavior) {
+  Base::CheckIsNotDestroyed();
   DCHECK_EQ(marking_behavior, kMarkContainerChain);
 
   // We need to dirty line box fragments only if the child is once laid out in
@@ -233,6 +262,8 @@
 
 template <typename Base>
 void LayoutNGBlockFlowMixin<Base>::UpdateNGBlockLayout() {
+  Base::CheckIsNotDestroyed();
+
   if (Base::IsOutOfFlowPositioned()) {
     LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout();
     return;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h
index 683b99fd..8459af4 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_block_flow_mixin.h
@@ -44,7 +44,7 @@
   NGInlineNodeData* GetNGInlineNodeData() const final;
   void ResetNGInlineNodeData() final;
   void ClearNGInlineNodeData() final;
-  bool HasNGInlineNodeData() const final { return ng_inline_node_data_; }
+  bool HasNGInlineNodeData() const final;
 
   LayoutUnit FirstLineBoxBaseline() const final;
   LayoutUnit InlineBlockBaseline(LineDirectionMode) const final;
@@ -62,7 +62,7 @@
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
 
 #if DCHECK_IS_ON()
-  void AddLayoutOverflowFromChildren() final { NOTREACHED(); }
+  void AddLayoutOverflowFromChildren() final;
 #endif
 
   void AddOutlineRects(Vector<PhysicalRect>&,
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
index fd83a427..7bb3909 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.cc
@@ -33,6 +33,7 @@
 
 template <typename Base>
 LayoutNGMixin<Base>::LayoutNGMixin(ContainerNode* node) : Base(node) {
+  Base::CheckIsNotDestroyed();
   static_assert(
       std::is_base_of<LayoutBlock, Base>::value,
       "Base class of LayoutNGMixin must be LayoutBlock or derived class.");
@@ -45,6 +46,8 @@
 
 template <typename Base>
 void LayoutNGMixin<Base>::Paint(const PaintInfo& paint_info) const {
+  Base::CheckIsNotDestroyed();
+
   // When |this| is NG block fragmented, the painter should traverse fragments
   // instead of |LayoutObject|, because this function cannot handle block
   // fragmented objects. We can come here only when |this| cannot traverse
@@ -78,6 +81,8 @@
                                       const HitTestLocation& hit_test_location,
                                       const PhysicalOffset& accumulated_offset,
                                       HitTestAction action) {
+  Base::CheckIsNotDestroyed();
+
   // See |Paint()|.
   DCHECK(Base::GetNGPaginationBreakability() ==
              LayoutNGBlockFlow::kForbidBreaks ||
@@ -97,6 +102,8 @@
 
 template <typename Base>
 RecalcLayoutOverflowResult LayoutNGMixin<Base>::RecalcLayoutOverflow() {
+  Base::CheckIsNotDestroyed();
+
   RecalcLayoutOverflowResult child_result;
   // Don't attempt to rebuild the fragment tree or recalculate
   // scrollable-overflow, layout will do this for us.
@@ -180,6 +187,7 @@
 
 template <typename Base>
 RecalcLayoutOverflowResult LayoutNGMixin<Base>::RecalcChildLayoutOverflow() {
+  Base::CheckIsNotDestroyed();
   DCHECK(Base::ChildNeedsLayoutOverflowRecalc());
   Base::ClearChildNeedsLayoutOverflowRecalc();
 
@@ -212,6 +220,7 @@
 
 template <typename Base>
 void LayoutNGMixin<Base>::RecalcVisualOverflow() {
+  Base::CheckIsNotDestroyed();
   if (Base::CanUseFragmentsForVisualOverflow()) {
     Base::RecalcFragmentsVisualOverflow();
     return;
@@ -220,7 +229,14 @@
 }
 
 template <typename Base>
+bool LayoutNGMixin<Base>::IsLayoutNGObject() const {
+  Base::CheckIsNotDestroyed();
+  return true;
+}
+
+template <typename Base>
 MinMaxSizes LayoutNGMixin<Base>::ComputeIntrinsicLogicalWidths() const {
+  Base::CheckIsNotDestroyed();
   DCHECK(!Base::IsTableCell());
 
   NGBlockNode node(const_cast<LayoutNGMixin<Base>*>(this));
@@ -236,6 +252,7 @@
 
 template <typename Base>
 NGConstraintSpace LayoutNGMixin<Base>::ConstraintSpaceForMinMaxSizes() const {
+  Base::CheckIsNotDestroyed();
   DCHECK(!Base::IsTableCell());
   const ComputedStyle& style = Base::StyleRef();
 
@@ -251,6 +268,8 @@
 
 template <typename Base>
 void LayoutNGMixin<Base>::UpdateOutOfFlowBlockLayout() {
+  Base::CheckIsNotDestroyed();
+
   auto* css_container = To<LayoutBoxModelObject>(Base::Container());
   LayoutBox* container = css_container->IsBox() ? To<LayoutBox>(css_container)
                                                 : Base::ContainingBlock();
@@ -370,6 +389,8 @@
 template <typename Base>
 scoped_refptr<const NGLayoutResult>
 LayoutNGMixin<Base>::UpdateInFlowBlockLayout() {
+  Base::CheckIsNotDestroyed();
+
   scoped_refptr<const NGLayoutResult> previous_result =
       Base::GetCachedLayoutResult();
   bool is_layout_root = !Base::View()->GetLayoutState()->Next();
@@ -410,6 +431,8 @@
 
 template <typename Base>
 void LayoutNGMixin<Base>::UpdateMargins() {
+  Base::CheckIsNotDestroyed();
+
   const LayoutBlock* containing_block = Base::ContainingBlock();
   if (!containing_block || !containing_block->IsLayoutBlockFlow())
     return;
diff --git a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
index 851ad5ff..585b148 100644
--- a/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
+++ b/third_party/blink/renderer/core/layout/ng/layout_ng_mixin.h
@@ -41,7 +41,7 @@
   RecalcLayoutOverflowResult RecalcChildLayoutOverflow() override;
   void RecalcVisualOverflow() override;
 
-  bool IsLayoutNGObject() const final { return true; }
+  bool IsLayoutNGObject() const final;
 
  protected:
   MinMaxSizes ComputeIntrinsicLogicalWidths() const override;
diff --git a/third_party/blink/renderer/core/probe/async_task_context.cc b/third_party/blink/renderer/core/probe/async_task_context.cc
index b530532ff..8245e69d 100644
--- a/third_party/blink/renderer/core/probe/async_task_context.cc
+++ b/third_party/blink/renderer/core/probe/async_task_context.cc
@@ -24,11 +24,8 @@
   // confidence that such a CHECK wouldn't break blink.
   isolate_ = context ? context->GetIsolate() : nullptr;
 
-  uint64_t trace_id = base::trace_event::GetNextGlobalTraceId();
-  SetTraceId(trace_id);
-  TRACE_EVENT("blink", "AsyncTask Scheduled", [&](perfetto::EventContext ctx) {
-    ctx.event()->add_flow_ids(trace_id);
-  });
+  TRACE_EVENT("blink", "AsyncTask Scheduled",
+              perfetto::Flow::FromPointer(this));
 
   if (!context)
     return;
diff --git a/third_party/blink/renderer/core/probe/async_task_context.h b/third_party/blink/renderer/core/probe/async_task_context.h
index cce70bc..8a791014 100644
--- a/third_party/blink/renderer/core/probe/async_task_context.h
+++ b/third_party/blink/renderer/core/probe/async_task_context.h
@@ -44,10 +44,6 @@
   void SetAdTask() { ad_task_ = true; }
   bool IsAdTask() const { return ad_task_; }
 
-  // Trace id for this task.
-  absl::optional<uint64_t> GetTraceId() const { return trace_id_; }
-  void SetTraceId(uint64_t trace_id) { trace_id_ = trace_id; }
-
   // The Id uniquely identifies this task with the V8 debugger. The Id is
   // calculated based on the address of `AsyncTaskContext`.
   void* Id() const;
@@ -56,7 +52,6 @@
   friend class AsyncTask;
 
   bool ad_task_ = false;
-  absl::optional<uint64_t> trace_id_;
   v8::Isolate* isolate_ = nullptr;
 };
 
diff --git a/third_party/blink/renderer/core/probe/core_probes.cc b/third_party/blink/renderer/core/probe/core_probes.cc
index 0d99851d..5f5574bb 100644
--- a/third_party/blink/renderer/core/probe/core_probes.cc
+++ b/third_party/blink/renderer/core/probe/core_probes.cc
@@ -67,7 +67,6 @@
                                    : nullptr),
       task_context_(task_context),
       recurring_(step),
-      tracing_(!!task_context->GetTraceId()),
       ad_tracker_(enabled && ad_tracking_type == AdTrackingType::kReport
                       ? AdTracker::FromExecutionContext(context)
                       : nullptr) {
@@ -75,12 +74,8 @@
   // not yet canceled. Currently we don't have enough confidence that such
   // a CHECK wouldn't break blink.
 
-  if (tracing_) {
-    TRACE_EVENT_BEGIN(
-        "blink", "AsyncTask Run", [&](perfetto::EventContext ctx) {
-          ctx.event()->add_flow_ids(task_context->GetTraceId().value());
-        });
-  }
+  TRACE_EVENT_BEGIN("blink", "AsyncTask Run",
+                    perfetto::Flow::FromPointer(task_context));
   if (debugger_)
     debugger_->AsyncTaskStarted(task_context->Id());
 
@@ -98,9 +93,7 @@
   if (ad_tracker_)
     ad_tracker_->DidFinishAsyncTask(task_context_);
 
-  if (tracing_) {
-    TRACE_EVENT_END("blink");  // "AsyncTask Run"
-  }
+  TRACE_EVENT_END("blink");  // "AsyncTask Run"
 }
 
 void AllAsyncTasksCanceled(ExecutionContext* context) {
diff --git a/third_party/blink/renderer/core/probe/core_probes.h b/third_party/blink/renderer/core/probe/core_probes.h
index 8eb093a..5c2e37a 100644
--- a/third_party/blink/renderer/core/probe/core_probes.h
+++ b/third_party/blink/renderer/core/probe/core_probes.h
@@ -110,7 +110,6 @@
   ThreadDebugger* debugger_;
   AsyncTaskContext* task_context_;
   bool recurring_;
-  bool tracing_ = false;
 
   // This persistent is safe since the class is STACK_ALLOCATED.
   Persistent<AdTracker> ad_tracker_;
diff --git a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
index 214b3a50..93abfc8d 100644
--- a/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
+++ b/third_party/blink/renderer/modules/credentialmanager/credentials_container.cc
@@ -854,24 +854,44 @@
           "one time."));
       return;
     }
-    case RequestIdTokenStatus::kErrorFedCmNotSupportedByProvider: {
-      resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kNotSupportedError,
-          "The indicated provider does not support FedCM."));
-      return;
-    }
-    case RequestIdTokenStatus::kErrorFetchingWellKnown: {
+    case RequestIdTokenStatus::kErrorFetchingWellKnownHttpNotFound: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kNetworkError,
-          "Error fetching the provider's .well-known configuration."));
+          "The provider's .well-known configuration cannot be found."));
       return;
     }
-    case RequestIdTokenStatus::kErrorInvalidWellKnown: {
+    case RequestIdTokenStatus::kErrorFetchingWellKnownNoResponse: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "The response body is empty when fetching the provider's .well-known "
+          "configuration."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingWellKnownInvalidResponse: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kInvalidStateError,
           "Provider's .well-known configuration is invalid."));
       return;
     }
+    case RequestIdTokenStatus::kErrorFetchingClientIdMetadataHttpNotFound: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "The provider's client metadata endpoint cannot be found."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingClientIdMetadataNoResponse: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "The response body is empty when fetching the provider's client "
+          "metadata."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingClientIdMetadataInvalidResponse: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kInvalidStateError,
+          "Provider's client metadata is invalid."));
+      return;
+    }
     case RequestIdTokenStatus::kErrorFetchingSignin: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kNetworkError,
@@ -881,24 +901,55 @@
     case RequestIdTokenStatus::kErrorInvalidSigninResponse: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kInvalidStateError,
-          "Provider's sign-in response is invalid"));
+          "Provider's sign-in response is invalid."));
       return;
     }
-    case RequestIdTokenStatus::kErrorInvalidAccountsResponse: {
+    case RequestIdTokenStatus::kErrorFetchingAccountsHttpNotFound: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kInvalidStateError,
-          "Provider's accounts response is invalid"));
+          DOMExceptionCode::kNetworkError,
+          "The provider's accounts list endpoint cannot be found."));
       return;
     }
-    case RequestIdTokenStatus::kErrorInvalidTokenResponse: {
+    case RequestIdTokenStatus::kErrorFetchingAccountsNoResponse: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "The response body is empty when fetching the provider's accounts "
+          "list."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingAccountsInvalidResponse: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
           DOMExceptionCode::kInvalidStateError,
-          "Provider's token response is invalid"));
+          "Provider's accounts list is invalid."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingIdTokenHttpNotFound: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "The provider's id token endpoint cannot be found."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingIdTokenNoResponse: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kNetworkError,
+          "The response body is empty when fetching the provider's id token."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingIdTokenInvalidResponse: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kInvalidStateError,
+          "Provider's id token is invalid."));
+      return;
+    }
+    case RequestIdTokenStatus::kErrorFetchingIdTokenInvalidRequest: {
+      resolver->Reject(MakeGarbageCollected<DOMException>(
+          DOMExceptionCode::kInvalidStateError,
+          "The id token fetching request is invalid."));
       return;
     }
     case RequestIdTokenStatus::kErrorCanceled: {
       resolver->Reject(MakeGarbageCollected<DOMException>(
-          DOMExceptionCode::kAbortError, "Request has been aborted"));
+          DOMExceptionCode::kAbortError, "The request has been aborted."));
       return;
     }
     case RequestIdTokenStatus::kError: {
diff --git a/third_party/blink/renderer/modules/direct_sockets/navigator_socket.cc b/third_party/blink/renderer/modules/direct_sockets/navigator_socket.cc
index 8f3c47ce..899c7ac 100644
--- a/third_party/blink/renderer/modules/direct_sockets/navigator_socket.cc
+++ b/third_party/blink/renderer/modules/direct_sockets/navigator_socket.cc
@@ -4,6 +4,7 @@
 
 #include "third_party/blink/renderer/modules/direct_sockets/navigator_socket.h"
 
+#include "base/metrics/histogram_functions.h"
 #include "net/base/net_errors.h"
 #include "services/network/public/mojom/tcp_socket.mojom-blink.h"
 #include "services/network/public/mojom/udp_socket.mojom-blink.h"
@@ -26,6 +27,13 @@
 
 namespace blink {
 
+namespace {
+
+constexpr char kPermissionDeniedHistogramName[] =
+    "DirectSockets.PermissionDeniedFailures";
+
+}  // namespace
+
 const char NavigatorSocket::kSupplementName[] = "NavigatorSocket";
 
 NavigatorSocket::NavigatorSocket(ExecutionContext* context)
@@ -170,6 +178,18 @@
     return false;
   }
 
+  // TODO(crbug.com/1119600): Do not consume (or check) transient activation
+  // for reconnection attempts.
+  if (!LocalFrame::ConsumeTransientUserActivation(window->GetFrame())) {
+    base::UmaHistogramEnumeration(
+        kPermissionDeniedHistogramName,
+        blink::mojom::blink::DirectSocketFailureType::kTransientActivation);
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kNotAllowedError,
+        "Must be handling a user gesture to open a socket.");
+    return false;
+  }
+
   DCHECK(options);
   if (!options->hasRemotePort()) {
     exception_state.ThrowTypeError("remotePort was not specified.");
diff --git a/third_party/blink/renderer/modules/webcodecs/BUILD.gn b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
index e95861b..4053a4ac 100644
--- a/third_party/blink/renderer/modules/webcodecs/BUILD.gn
+++ b/third_party/blink/renderer/modules/webcodecs/BUILD.gn
@@ -22,6 +22,7 @@
     "audio_encoder.cc",
     "audio_encoder.h",
     "codec_config_eval.h",
+    "codec_logger.cc",
     "codec_logger.h",
     "codec_state_helper.cc",
     "codec_state_helper.h",
diff --git a/third_party/blink/renderer/modules/webcodecs/DEPS b/third_party/blink/renderer/modules/webcodecs/DEPS
index ff7d33c..3071c2a 100644
--- a/third_party/blink/renderer/modules/webcodecs/DEPS
+++ b/third_party/blink/renderer/modules/webcodecs/DEPS
@@ -1,5 +1,6 @@
 include_rules = [
     "+base/bind.h",
+    "+base/strings/string_util.h",
     "+base/test",
     "+base/threading/sequenced_task_runner_handle.h",
     "+base/threading/thread_task_runner_handle.h",
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
index a9632a7..268ec1c1 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_decoder.cc
@@ -97,8 +97,6 @@
 void AudioDecoderTraits::UpdateDecoderLog(const MediaDecoderType& decoder,
                                           const MediaConfigType& media_config,
                                           media::MediaLog* media_log) {
-  media_log->SetProperty<media::MediaLogProperty::kFrameTitle>(
-      std::string("AudioDecoder(WebCodecs)"));
   media_log->SetProperty<media::MediaLogProperty::kAudioDecoderName>(
       decoder.GetDecoderType());
   media_log->SetProperty<media::MediaLogProperty::kIsPlatformAudioDecoder>(
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
index f48f0607..48d4157 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.cc
@@ -140,11 +140,6 @@
 }  // namespace
 
 // static
-const char* AudioEncoderTraits::GetNameForDevTools() {
-  return "AudioEncoder(WebCodecs)";
-}
-
-// static
 const char* AudioEncoderTraits::GetName() {
   return "AudioEncoder";
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/audio_encoder.h b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
index 5c0c56e..6b660e0 100644
--- a/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/audio_encoder.h
@@ -48,7 +48,6 @@
   using MediaEncoder = media::AudioEncoder;
 
   // Can't be a virtual method, because it's used from base ctor.
-  static const char* GetNameForDevTools();
   static const char* GetName();
 };
 
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.cc b/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
new file mode 100644
index 0000000..ec513f8
--- /dev/null
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.cc
@@ -0,0 +1,42 @@
+// 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/modules/webcodecs/codec_logger.h"
+#include "base/strings/string_util.h"
+
+namespace blink {
+
+namespace internal {
+
+std::string SanitizeStringProperty(WebString value) {
+  std::string converted = value.Utf8();
+  return base::IsStringUTF8(converted) ? converted : "[invalid property]";
+}
+
+void SendPlayerNameInformationInternal(media::MediaLog* media_log,
+                                       const ExecutionContext& context,
+                                       std::string loadedAs) {
+  media_log->AddEvent<media::MediaLogEvent::kLoad>("Webcodecs::" + loadedAs);
+  WebString frame_title;
+  if (context.IsWindow()) {
+    const auto& dom_context = To<LocalDOMWindow>(context);
+    frame_title = dom_context.name();
+    if (!frame_title.length()) {
+      auto* frame = WebLocalFrameImpl::FromFrame(dom_context.GetFrame());
+      if (frame)
+        frame_title = frame->GetDocument().Title();
+    }
+  } else if (context.IsWorkerOrWorkletGlobalScope()) {
+    const auto& worker_context = To<WorkerOrWorkletGlobalScope>(context);
+    frame_title = worker_context.Name();
+    if (!frame_title.length())
+      frame_title = worker_context.Url().GetString();
+  }
+  media_log->SetProperty<media::MediaLogProperty::kFrameTitle>(
+      internal::SanitizeStringProperty(frame_title));
+}
+
+}  // namespace internal
+
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/webcodecs/codec_logger.h b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
index aee4e6d..2d389c1 100644
--- a/third_party/blink/renderer/modules/webcodecs/codec_logger.h
+++ b/third_party/blink/renderer/modules/webcodecs/codec_logger.h
@@ -12,7 +12,10 @@
 #include "media/base/status.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
+#include "third_party/blink/renderer/core/frame/local_dom_window.h"
+#include "third_party/blink/renderer/core/frame/web_local_frame_impl.h"
 #include "third_party/blink/renderer/core/inspector/inspector_media_context_impl.h"
+#include "third_party/blink/renderer/core/workers/worklet_global_scope.h"
 #include "third_party/blink/renderer/modules/modules_export.h"
 
 namespace base {
@@ -21,6 +24,14 @@
 
 namespace blink {
 
+namespace internal {
+
+void SendPlayerNameInformationInternal(media::MediaLog* media_log,
+                                       const ExecutionContext& context,
+                                       std::string loadedAs);
+
+}  // namespace internal
+
 class ExecutionContext;
 
 // Simple wrapper around MediaLog instances, to manage the lifetime safety of
@@ -62,6 +73,12 @@
 
   ~CodecLogger() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
 
+  void SendPlayerNameInformation(const ExecutionContext& context,
+                                 std::string loadedAs) {
+    internal::SendPlayerNameInformationInternal(media_log_.get(), context,
+                                                loadedAs);
+  }
+
   // Creates an OperationError DOMException with the given |error_msg|, and logs
   // the given |status| in |media_log_|.
   // Since |status| can come from platform codecs, its contents won't be
diff --git a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
index 92a708c..0e7c22d 100644
--- a/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
+++ b/third_party/blink/renderer/modules/webcodecs/decoder_template.cc
@@ -636,6 +636,8 @@
   if (is_flush) {
     pending_request_->resolver.Release()->Resolve();
   } else {
+    logger_->SendPlayerNameInformation(*GetExecutionContext(),
+                                       Traits::GetName());
     Traits::UpdateDecoderLog(*decoder_, *pending_request_->media_config,
                              logger_->log());
 
diff --git a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
index a800ed5..7b2268d 100644
--- a/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
+++ b/third_party/blink/renderer/modules/webcodecs/encoder_base.cc
@@ -68,9 +68,7 @@
                                                          callback_runner_);
 
   media::MediaLog* log = logger_->log();
-
-  log->SetProperty<media::MediaLogProperty::kFrameTitle>(
-      std::string(Traits::GetNameForDevTools()));
+  logger_->SendPlayerNameInformation(*context, Traits::GetName());
   log->SetProperty<media::MediaLogProperty::kFrameUrl>(
       GetExecutionContext()->Url().GetString().Ascii());
 
diff --git a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
index c05a145..0540e604 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_decoder.cc
@@ -282,8 +282,6 @@
 void VideoDecoderTraits::UpdateDecoderLog(const MediaDecoderType& decoder,
                                           const MediaConfigType& media_config,
                                           media::MediaLog* media_log) {
-  media_log->SetProperty<media::MediaLogProperty::kFrameTitle>(
-      std::string("VideoDecoder(WebCodecs)"));
   media_log->SetProperty<media::MediaLogProperty::kVideoDecoderName>(
       decoder.GetDecoderType());
   media_log->SetProperty<media::MediaLogProperty::kIsPlatformVideoDecoder>(
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
index 75be6ca..31f31babb 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.cc
@@ -428,11 +428,6 @@
 }  // namespace
 
 // static
-const char* VideoEncoderTraits::GetNameForDevTools() {
-  return "VideoEncoder(WebCodecs)";
-}
-
-// static
 const char* VideoEncoderTraits::GetName() {
   return "VideoEncoder";
 }
diff --git a/third_party/blink/renderer/modules/webcodecs/video_encoder.h b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
index 1b3453d..2b4f9e0 100644
--- a/third_party/blink/renderer/modules/webcodecs/video_encoder.h
+++ b/third_party/blink/renderer/modules/webcodecs/video_encoder.h
@@ -57,7 +57,6 @@
   using MediaEncoder = media::VideoEncoder;
 
   // Can't be a virtual method, because it's used from base ctor.
-  static const char* GetNameForDevTools();
   static const char* GetName();
 };
 
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 385b895..747f215 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -7888,7 +7888,6 @@
 crbug.com/1249176 [ Mac11-arm64 ] http/tests/local/stylesheet-and-script-load-order-http.html [ Crash ]
 crbug.com/1249176 [ Mac11-arm64 ] http/tests/local/blob/send-sliced-data-blob.html [ Crash ]
 crbug.com/1249176 [ Mac11-arm64 ] http/tests/local/formdata/send-form-data-with-empty-name.html [ Crash ]
-crbug.com/1249176 [ Mac11-arm64 ] http/tests/security/xss-DENIED-assign-location-hostname.html [ Crash ]
 crbug.com/1249176 [ Mac11-arm64 ] media/picture-in-picture/v2/request-picture-in-picture.html [ Crash ]
 crbug.com/1249176 [ Mac11-arm64 ] paint/invalidation/selection/invalidation-rect-includes-newline-for-rtl.html [ Crash ]
 crbug.com/1249176 [ Mac11-arm64 ] storage/indexeddb/intversion-revert-on-abort.html [ Crash ]
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js
new file mode 100644
index 0000000..c5bed0f
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/child-navigates-parent-cross-origin.window.js
@@ -0,0 +1,90 @@
+// META: script=/common/get-host-info.sub.js
+// META: script=resources/wait-for-messages.js
+
+function testNavigationFails(params) {
+  return async (t) => {
+    // Start waiting for messages before inserting the child frame, to avoid any
+    // race conditions. Note that this would be racy if we executed tests
+    // concurrently, thankfully `promise_test` executes sequentially. See also:
+    // https://github.com/web-platform-tests/rfcs/pull/75
+    const messagesPromise = waitForMessages(1);
+
+    // Execute the test in an iframe, so that the document executing the test
+    // is not navigated away mid-test in case of failure.
+    const child = document.createElement("iframe");
+    document.body.appendChild(child);
+    t.add_cleanup(() => { document.body.removeChild(child); });
+
+    const url = new URL(
+        "resources/child-navigates-parent-cross-origin-inner.html",
+        window.location);
+
+    // Load the grandchild iframe from a different origin.
+    url.host = get_host_info().REMOTE_HOST;
+
+    for (const key in params || {}) {
+      url.searchParams.set(key, params[key]);
+    }
+
+    const grandchild = child.contentDocument.createElement("iframe");
+    grandchild.src = url;
+    child.contentDocument.body.appendChild(grandchild);
+
+    const messages = await messagesPromise;
+    assert_array_equals(messages, ["error: SecurityError"]);
+  }
+}
+
+promise_test(
+    testNavigationFails(),
+    "Child document attempts to navigate cross-origin parent via location");
+
+promise_test(
+    testNavigationFails({ "property": "hash" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.hash");
+
+promise_test(
+    testNavigationFails({ "property": "host" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.host");
+
+promise_test(
+    testNavigationFails({ "property": "hostname" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.hostname");
+
+promise_test(
+    testNavigationFails({ "property": "href" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.href");
+
+promise_test(
+    testNavigationFails({ "property": "pathname" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.pathname");
+
+promise_test(
+    testNavigationFails({ "property": "protocol" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.protocol");
+
+promise_test(
+    testNavigationFails({ "property": "reload" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.reload()");
+
+promise_test(
+    testNavigationFails({ "property": "replace" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.replace()");
+
+promise_test(
+    testNavigationFails({ "property": "search" }),
+    "Child document attempts to navigate cross-origin parent via "+
+    "location.search");
+
+promise_test(
+    testNavigationFails({ "property": "xxxNonExistent" }),
+    "Child document attempts to navigate cross-origin parent via non-standard "+
+    "location property");
diff --git a/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html
new file mode 100644
index 0000000..72b92c80
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/browsers/browsing-the-web/navigating-across-documents/resources/child-navigates-parent-cross-origin-inner.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<script>
+const params = new URL(window.location).searchParams;
+const property = params.get("property");
+
+try {
+  if (property === null) {
+    parent.location = "foo";
+  } else if (property === "reload") {
+    parent.location.reload();
+  } else if (property === "replace") {
+    parent.location.replace("foo");
+  } else {
+    parent.location[property] = "foo";
+  }
+  parent.parent.postMessage("success", "*");
+} catch (e) {
+  parent.parent.postMessage(`error: ${e.name}`, "*");
+}
+</script>
diff --git a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png
index d289c9bb..d0305f5 100644
--- a/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png
+++ b/third_party/blink/web_tests/flag-specific/disable-layout-ng/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hash-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hash-attacker.html
deleted file mode 100644
index b3e8e4a4..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hash-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.hash = "hax0red";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-host-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-host-attacker.html
deleted file mode 100644
index 0cb5e6fb..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-host-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.host = "localhost:8000/security/resources/xss-DENIED-assign-location-host-failure.html?";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-host-failure.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-host-failure.html
deleted file mode 100644
index 3cc9466e..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-host-failure.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<script>
-    if (window.testRunner)
-        testRunner.notifyDone();
-</script>
-
-FAIL: cross-site assignment of location.host was allowed.
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hostname-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hostname-attacker.html
deleted file mode 100644
index 6b58fcb..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hostname-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.hostname = "localhost:8000/security/resources/xss-DENIED-assign-location-hostname-failure.html?";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hostname-failure.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hostname-failure.html
deleted file mode 100644
index 4ab2e44..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-hostname-failure.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<script>
-    if (window.testRunner)
-        testRunner.notifyDone();
-</script>
-
-FAIL: cross-site assignment of location.hostname was allowed.
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-nonstandardProperty-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-nonstandardProperty-attacker.html
deleted file mode 100644
index 44472aa2..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-nonstandardProperty-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.nonstandardProperty = "hax0red";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-pathname-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-pathname-attacker.html
deleted file mode 100644
index 537f630..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-pathname-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.pathname = "/security/resources/xss-DENIED-assign-location-pathname-failure.html";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-pathname-failure.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-pathname-failure.html
deleted file mode 100644
index 11e619b..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-pathname-failure.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<script>
-    if (window.testRunner)
-        testRunner.notifyDone();
-</script>
-
-FAIL: cross-site assignment of location.pathname was allowed.
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-protocol-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-protocol-attacker.html
deleted file mode 100644
index a80c340..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-protocol-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.protocol = "http://localhost:8000/security/resources/xss-DENIED-assign-location-protocol-failure.html?";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-protocol-failure.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-protocol-failure.html
deleted file mode 100644
index 478ce381..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-protocol-failure.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<script>
-    if (window.testRunner)
-        testRunner.notifyDone();
-</script>
-
-FAIL: cross-site assignment of location.protocol was allowed.
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-reload-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-reload-attacker.html
deleted file mode 100644
index c2504ca..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-reload-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.reload = "hax0red";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-search-attacker.html b/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-search-attacker.html
deleted file mode 100644
index e366e83..0000000
--- a/third_party/blink/web_tests/http/tests/security/resources/xss-DENIED-assign-location-search-attacker.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<script>
-var victim = parent;
-
-victim.location.search = "?hax0red";
-</script>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hash-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hash-expected.txt
deleted file mode 100644
index f8ce554..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hash-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.hash not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hash.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hash.html
deleted file mode 100644
index d26c26c47..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hash.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function finish()
-{
-    if (location.hash.length == 0)
-        document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.hash not allowed";
-    else
-        document.getElementById("console").innerHTML = "FAIL: cross-site assignment of location.hash was allowed!";
-
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="finish()">
-
-    <iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-hash-attacker.html"></iframe>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-host-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-host-expected.txt
deleted file mode 100644
index 8532c3d..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-host-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.host not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-host.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-host.html
deleted file mode 100644
index 04ea04c4..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-host.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function success()
-{
-    document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.host not allowed";
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="success()">
-
-    <iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-host-attacker.html"></iframe>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hostname-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hostname-expected.txt
deleted file mode 100644
index 0463c32..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hostname-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.hostname not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hostname.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hostname.html
deleted file mode 100644
index cd57a35..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-hostname.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function success()
-{
-    document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.hostname not allowed";
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="success()">
-
-    <iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-hostname-attacker.html"></iframe>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-nonstandardProperty-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-nonstandardProperty-expected.txt
deleted file mode 100644
index bd6b96b..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-nonstandardProperty-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.nonstandardProperty not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-nonstandardProperty.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-nonstandardProperty.html
deleted file mode 100644
index 43f5df30..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-nonstandardProperty.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function finish()
-{
-    if (location.nonstandardProperty != "hax0red")
-        document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.nonstandardProperty not allowed";
-    else
-        document.getElementById("console").innerHTML = "FAIL: cross-site assignment of location.nonstandardProperty was allowed!";
-
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="finish()">
-
-    <iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-nonstandardProperty-attacker.html"></iframe>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-pathname-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-pathname-expected.txt
deleted file mode 100644
index 4521070..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-pathname-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.pathname not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-pathname.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-pathname.html
deleted file mode 100644
index 6b8343eb..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-pathname.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function success()
-{
-    document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.pathname not allowed";
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="success()">
-
-    <iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-pathname-attacker.html"></iframe>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-protocol-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-protocol-expected.txt
deleted file mode 100644
index d8f3cc7..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-protocol-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.protocol not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-protocol.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-protocol.html
deleted file mode 100644
index 0e61396..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-protocol.html
+++ /dev/null
@@ -1,21 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function success()
-{
-    document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.protocol not allowed";
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="success()">
-
-    <iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-protocol-attacker.html"></iframe>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-reload-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-reload-expected.txt
deleted file mode 100644
index 1d3f858..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-reload-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.replace not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-reload.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-reload.html
deleted file mode 100644
index c25b34c..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-reload.html
+++ /dev/null
@@ -1,25 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function finish()
-{
-    if (location.reload != "hax0red")
-        document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.replace not allowed";
-    else
-        document.getElementById("console").innerHTML = "FAIL: cross-site assignment of location.replace was allowed!";
-
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="finish()">
-
-    <iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-reload-attacker.html"></iframe>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-search-expected.txt b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-search-expected.txt
deleted file mode 100644
index df918c3..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-search-expected.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-CONSOLE ERROR: Uncaught SecurityError: Blocked a frame with origin "http://localhost:8000" from accessing a frame with origin "http://127.0.0.1:8000". Protocols, domains, and ports must match.
-PASS: cross-site assignment of location.search not allowed
diff --git a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-search.html b/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-search.html
deleted file mode 100644
index 786b03d..0000000
--- a/third_party/blink/web_tests/http/tests/security/xss-DENIED-assign-location-search.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<script>
-if (window.testRunner) {
-    testRunner.dumpAsText();
-    testRunner.waitUntilDone();
-}
-
-function finish()
-{
-    if (location.search.length == 0)
-        document.getElementById("console").innerHTML = "PASS: cross-site assignment of location.search not allowed";
-    else
-        document.getElementById("console").innerHTML = "FAIL: cross-site assignment of location.search was allowed!";
-
-    if (window.testRunner)
-        testRunner.notifyDone();
-}
-</script>
-
-<body onload="finish()">
-
-<script>
-if (location.search == 0) {
-    document.write('<iframe src="http://localhost:8000/security/resources/xss-DENIED-assign-location-search-attacker.html"></iframe>');
-}
-</script>
-
-<div id="console"></div>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png b/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png
index d289c9bb..d0305f5 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/canvas-text-baseline-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png b/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png
index 78e13f6..b02a634 100644
--- a/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png
+++ b/third_party/blink/web_tests/platform/linux/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac-mac10.12/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.12/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt
new file mode 100644
index 0000000..2e0403bd
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.12/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt
@@ -0,0 +1,3 @@
+Tests DOM.getFrameOwner method.
+IFRAME id,inner_frame,src,http://devtools.oopif.test:8000/resources/dummy.html
+
diff --git a/third_party/blink/web_tests/platform/mac-mac10.13/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt b/third_party/blink/web_tests/platform/mac-mac10.13/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt
new file mode 100644
index 0000000..aa3db55
--- /dev/null
+++ b/third_party/blink/web_tests/platform/mac-mac10.13/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt
@@ -0,0 +1,5 @@
+Tests DOM.getFrameOwner method.
+Error while executing test script: TypeError: Cannot read properties of undefined (reading 'node')
+TypeError: Cannot read properties of undefined (reading 'node')
+    at eval (http://127.0.0.1:8000/inspector-protocol/dom/dom-getFrameOwner.js:16:27)
+
diff --git a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-expected.png b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-expected.png
index 3aa43c5c..ec947bef 100644
--- a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-shadow-expected.png b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-shadow-expected.png
index a8c646f..694dd1c7 100644
--- a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-shadow-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-composite-video-shadow-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
deleted file mode 100644
index 84fc034..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
index 84951e3..48946c73 100644
--- a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-imageSmoothingEnabled-patterns-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
deleted file mode 100644
index 78a2bd3..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
deleted file mode 100644
index 61fb575..0000000
--- a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
+++ /dev/null
Binary files differ
diff --git a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png
index bc9dbe8..fe691ad 100644
--- a/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png
+++ b/third_party/blink/web_tests/platform/win/virtual/oopr-canvas2d/fast/canvas/quadraticCurveTo-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
rename to third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-ellipse-connecting-line-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/oopr-canvas2d/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/virtual/oopr-canvas2d/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
rename to third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-pattern-no-repeat-with-transformations-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/platform/mac/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png b/third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
similarity index 100%
rename from third_party/blink/web_tests/platform/mac/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
rename to third_party/blink/web_tests/virtual/oopr-canvas2d/fast/canvas/canvas-zero-length-lineCap-expected.png
Binary files differ
diff --git a/third_party/blink/web_tests/virtual/plz-dedicated-worker/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt b/third_party/blink/web_tests/virtual/plz-dedicated-worker/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt
new file mode 100644
index 0000000..2e0403bd
--- /dev/null
+++ b/third_party/blink/web_tests/virtual/plz-dedicated-worker/http/tests/inspector-protocol/dom/dom-getFrameOwner-expected.txt
@@ -0,0 +1,3 @@
+Tests DOM.getFrameOwner method.
+IFRAME id,inner_frame,src,http://devtools.oopif.test:8000/resources/dummy.html
+
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 43c54a1..d82e049 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3028,6 +3028,14 @@
   <int value="2" label="Apps Page context menu"/>
 </enum>
 
+<enum name="AppInputEventSource">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Mouse"/>
+  <int value="2" label="Stylus"/>
+  <int value="3" label="Touch"/>
+  <int value="4" label="Keyboard"/>
+</enum>
+
 <enum name="AppLaunch">
   <int value="0" label="NTP_APPS_MAXIMIZED"/>
   <int value="1" label="NTP_APPS_COLLAPSED"/>
@@ -4961,6 +4969,16 @@
   <int value="9" label="STATUS_DBUS_ERROR"/>
 </enum>
 
+<enum name="AttributionEvent">
+  <int value="0" label="Received With Native"/>
+  <int value="1" label="Cached Pre-Native"/>
+  <int value="2" label="Reported Post-Native"/>
+  <int value="3" label="Dropped - Storage Full"/>
+  <int value="4" label="Dropped - Write Failed"/>
+  <int value="5" label="Dropped - Read Failed"/>
+  <int value="6" label="Write File Close Failed"/>
+</enum>
+
 <enum name="AuctionResult">
   <int value="0" label="Success"/>
   <int value="1" label="Aborted"/>
@@ -78272,6 +78290,12 @@
   <int value="2" label="Enabled to none"/>
 </enum>
 
+<enum name="SegmentationModelAvailability">
+  <int value="0" label="Model handler created"/>
+  <int value="1" label="Model available"/>
+  <int value="2" label="Model metadata invalid"/>
+</enum>
+
 <enum name="SegmentationPlatformModelExecutionStatus">
   <int value="0" label="Success"/>
   <int value="1" label="ExecutionError"/>
@@ -78302,6 +78326,21 @@
   <int value="9" label="FeatureNameHashDoesNotMatchName"/>
 </enum>
 
+<enum name="SegmentationSelectionFailureReason">
+  <int value="0" label="Segmentation platform is disabled"/>
+  <int value="1" label="Segment selection available in prefs"/>
+  <int value="2" label="At least one segment is not ready"/>
+  <int value="3"
+      label="At least one segment's signal collection is not complete"/>
+  <int value="4" label="Segment selection TTL has not expired"/>
+  <int value="5" label="At least one model failed to execute"/>
+  <int value="6" label="At least one model needs more signals"/>
+  <int value="7" label="At least one model has invalid metadata"/>
+  <int value="8" label="Failed to write model result to DB"/>
+  <int value="9" label="Invalid selection result found in prefs"/>
+  <int value="10" label="Database initialization failed"/>
+</enum>
+
 <enum name="SelectedNewTabCreationOption">
   <int value="0" label="Open in new tab is first, selected open in new tab"/>
   <int value="1"
@@ -79777,6 +79816,66 @@
   <int value="17" label="Not registered"/>
 </enum>
 
+<enum name="ShillConnectResult">
+  <int value="0" label="Unknown"/>
+  <int value="1" label="Success"/>
+  <int value="2" label="Aaa Failed"/>
+  <int value="3" label="Activation Failed"/>
+  <int value="4" label="Bad Passphrase"/>
+  <int value="5" label="Bad WEP Key"/>
+  <int value="6" label="Connect Failed"/>
+  <int value="7" label="DNS Lookup Failed"/>
+  <int value="8" label="Dhcp Failed"/>
+  <int value="9" label="HTTP Get Failed"/>
+  <int value="10" label="Internal"/>
+  <int value="11" label="Invalid Failure"/>
+  <int value="12" label="Ipsec Cert Auth Failed"/>
+  <int value="13" label="Ipsec Psk Auth Failed"/>
+  <int value="14" label="Need Evdo"/>
+  <int value="15" label="Need Home Network"/>
+  <int value="16" label="No Failure"/>
+  <int value="17" label="Not Associated"/>
+  <int value="18" label="Not Authenticated"/>
+  <int value="19" label="Otasp Failed"/>
+  <int value="20" label="Out Of Range"/>
+  <int value="21" label="Pin Missing"/>
+  <int value="22" label="Ppp Auth Failed"/>
+  <int value="23" label="Sim Locked"/>
+  <int value="24" label="Not Registered"/>
+  <int value="25" label="Too Many STAs"/>
+  <int value="26" label="Disconnect"/>
+  <int value="27" label="Unknown Failure"/>
+  <int value="28" label="Result Failure"/>
+  <int value="29" label="Result Already Connected"/>
+  <int value="30" label="Result Already Exists"/>
+  <int value="31" label="Result IncorrectPin"/>
+  <int value="32" label="Result In Progress"/>
+  <int value="33" label="Result Internal"/>
+  <int value="34" label="Result Invalid Apn"/>
+  <int value="35" label="Result Invalid Arguments"/>
+  <int value="36" label="Result Invalid NetworkName"/>
+  <int value="37" label="Result Invalid Passphrase"/>
+  <int value="38" label="Result Invalid Property"/>
+  <int value="39" label="Result No Carrier"/>
+  <int value="40" label="Result Not Connected"/>
+  <int value="41" label="Result Not Found"/>
+  <int value="42" label="Result Not Implemented"/>
+  <int value="43" label="Result Not On Home Network"/>
+  <int value="44" label="Result Not Registered"/>
+  <int value="45" label="Result Not Supported"/>
+  <int value="46" label="Result Operation Aborted"/>
+  <int value="47" label="Result Operation Initiated"/>
+  <int value="48" label="Result Operation Timeout"/>
+  <int value="49" label="Result Passphrase Required"/>
+  <int value="50" label="Result Permission Denied"/>
+  <int value="51" label="Result Pin Blocked"/>
+  <int value="52" label="Result Pin Required"/>
+  <int value="53" label="Result Wrong State"/>
+  <int value="54" label="Eap Authentication Failed"/>
+  <int value="55" label="Eap Local Tls Failed"/>
+  <int value="56" label="Eap Remote Tls Failed"/>
+</enum>
+
 <enum name="ShillSuspendTerminationDarkResumeActionResult">
   <summary>
     The termination/suspend/dark resume action result types come from
@@ -90251,6 +90350,11 @@
   <int value="4" label="Toolbar button"/>
 </enum>
 
+<enum name="VPNConfigurationSource">
+  <int value="0" label="Configured manually"/>
+  <int value="1" label="Configured by policy"/>
+</enum>
+
 <enum name="VPNDriver">
   <int value="0" label="OpenVPN"/>
   <int value="1" label="L2TP/IPSec"/>
diff --git a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
index d97fa82..c557407c 100644
--- a/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+++ b/tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
@@ -16,6 +16,7 @@
 danielng@google.com
 davidmunro@google.com
 dewittj@chromium.org
+dmblack@google.com
 dmurph@chromium.org
 drubery@chromium.org
 dschinazi@chromium.org
diff --git a/tools/metrics/histograms/metadata/enterprise/histograms.xml b/tools/metrics/histograms/metadata/enterprise/histograms.xml
index 993c3e73..689ec02 100644
--- a/tools/metrics/histograms/metadata/enterprise/histograms.xml
+++ b/tools/metrics/histograms/metadata/enterprise/histograms.xml
@@ -1996,6 +1996,29 @@
   <summary>Tracking the results of policy user verification.</summary>
 </histogram>
 
+<histogram name="Enterprise.ProfileSeparation.DasherPolicyFetch.HttpResponse"
+    enum="HttpResponseCode" expires_after="2022-12-08">
+  <owner>ydago@chromium.org</owner>
+  <owner>zmin@chromium.org</owner>
+  <summary>
+    The Http statuses returned by the server when trying to get the
+    ManagedAccounsSigninRestriction policy value for individual dasher accounts.
+    This is recorded after trying to the the policy value, when the status is
+    available.
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.ProfileSeparation.DasherPolicyFetch.NetworkError"
+    enum="NetErrorCodes" expires_after="2022-12-08">
+  <owner>ydago@chromium.org</owner>
+  <owner>zmin@chromium.org</owner>
+  <summary>
+    The network errors that happen when trying to get the
+    ManagedAccounsSigninRestriction policy value for individual dasher accounts.
+    This is recorded every time after trying to the the policy value.
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.PublicSession.ExtensionPermissions"
     enum="ExtensionPermission3" expires_after="2022-03-01">
   <owner>anqing@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/holding_space/OWNER b/tools/metrics/histograms/metadata/holding_space/OWNER
new file mode 100644
index 0000000..59024b0b
--- /dev/null
+++ b/tools/metrics/histograms/metadata/holding_space/OWNER
@@ -0,0 +1,5 @@
+per-file OWNERS=file://tools/metrics/histograms/metadata/METRIC_REVIEWER_OWNERS
+
+# Prefer sending CLs to the owners listed below.
+# Use chromium-metrics-reviews@google.com as a backup.
+dmblack@google.com
diff --git a/tools/metrics/histograms/metadata/network/histograms.xml b/tools/metrics/histograms/metadata/network/histograms.xml
index 02a69e5..8941175 100644
--- a/tools/metrics/histograms/metadata/network/histograms.xml
+++ b/tools/metrics/histograms/metadata/network/histograms.xml
@@ -89,6 +89,51 @@
   <summary>Errors experienced during Gobi device powerup.</summary>
 </histogram>
 
+<histogram name="Network.Ash.VPN.{VPNProviderType}.ConfigurationSource"
+    enum="VPNConfigurationSource" expires_after="2022-12-31">
+  <owner>chadduffin@chromium.org</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <summary>
+    Logs the configuration source (i.e., the user vs. policy) for VPN networks.
+    Emitted when a VPN is configured.
+  </summary>
+  <token key="VPNProviderType">
+    <variant name="ARC"/>
+    <variant name="L2TPIPsec"/>
+    <variant name="OpenVpn"/>
+    <variant name="ThirdParty"/>
+    <variant name="WireGuard"/>
+  </token>
+</histogram>
+
+<histogram name="Network.Ash.{NetworkType}.ConnectionResult.All"
+    enum="ShillConnectResult" expires_after="2022-11-01">
+  <owner>hsuregan@chromium.org</owner>
+  <owner>cros-connectivity@google.com</owner>
+  <summary>
+    Tracks the result of all Network connection attempts. A success is emitted
+    when a network of type {NetworkType} becomes connected from a non-connected
+    state. When a network of type {NetworkType} goes from a connecting state to
+    a disconnected state without a user initiated disconnect, a shill failure
+    reason is emitted.
+  </summary>
+  <token key="NetworkType">
+    <variant name="Cellular"/>
+    <variant name="Cellular.ESim"/>
+    <variant name="Cellular.PSim"/>
+    <variant name="Ethernet"/>
+    <variant name="Ethernet.Eap"/>
+    <variant name="Ethernet.NoEap"/>
+    <variant name="Tether"/>
+    <variant name="VPN"/>
+    <variant name="VPN.TypeBuiltIn"/>
+    <variant name="VPN.TypeThirdParty"/>
+    <variant name="WiFi"/>
+    <variant name="WiFi.SecurityOpen"/>
+    <variant name="WiFi.SecurityPasswordProtected"/>
+  </token>
+</histogram>
+
 <histogram name="Network.Cellular.Activation.StatusAtLogin"
     enum="NetworkCellularActivationState" expires_after="2021-08-29">
   <obsolete>
diff --git a/tools/metrics/histograms/metadata/others/histograms.xml b/tools/metrics/histograms/metadata/others/histograms.xml
index 3dd210e..6f4f4ee 100644
--- a/tools/metrics/histograms/metadata/others/histograms.xml
+++ b/tools/metrics/histograms/metadata/others/histograms.xml
@@ -3839,6 +3839,21 @@
   </summary>
 </histogram>
 
+<histogram name="Conversions.AppToWeb.AttributionEvents"
+    enum="AttributionEvent" expires_after="2022-12-08">
+  <owner>johnidel@chromium.org</owner>
+  <owner>mthiesse@chromium.org</owner>
+  <owner>measurement-api-dev+metrics@google.com</owner>
+  <summary>
+    Records events for the App To Web Impressions ContentProvider in order to
+    track how many attributions are received/lost/reported to the native
+    conversions code. Note that reported post-native only counts impressions
+    that were cached pre-native, not all reported impressions.
+
+    A single impression may emit multiple different attribution events.
+  </summary>
+</histogram>
+
 <histogram name="Conversions.ClearDataTime" units="ms" expires_after="M105">
   <owner>johnidel@chromium.org</owner>
   <owner>csharrison@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/password/histograms.xml b/tools/metrics/histograms/metadata/password/histograms.xml
index e6c0d6f7..ead501ec 100644
--- a/tools/metrics/histograms/metadata/password/histograms.xml
+++ b/tools/metrics/histograms/metadata/password/histograms.xml
@@ -2393,40 +2393,6 @@
   </token>
 </histogram>
 
-<histogram
-    name="PasswordManager.PasswordStoreProxyBackend.{ModifyingFunction}.{Metric}.{Measurement}"
-    units="count" expires_after="2022-06-30">
-  <owner>fhorschig@chromium.org</owner>
-  <owner>vasilii@chromium.org</owner>
-  <summary>
-    Records the number of password store changes {Metric}{Measurement} Recorded
-    when the asynchronous job for {ModifyingFunction} has returned. Recorded
-    only for non-syncing users and only after initial migration is finished.
-  </summary>
-  <token key="ModifyingFunction">
-    <variant name="AddLoginAsync" summary="AddLoginAsync()"/>
-  </token>
-  <token key="Metric">
-    <variant name="Diff"
-        summary="in either the main or the shadow backend but not in both of
-                 them"/>
-    <variant name="InconsistentPasswords"
-        summary="that occur in both the main and the shadow backend but
-                 differ in their stored passwords"/>
-    <variant name="MainMinusShadow"
-        summary="in the main backend that are not in the shadow backend"/>
-    <variant name="ShadowMinusMain"
-        summary="in the shadow backend that are not the main backend"/>
-  </token>
-  <token key="Measurement">
-    <variant name="Abs" summary="."/>
-    <variant name="Rel"
-        summary=", divided by the total number of password store changes, as
-                 a percentage rounded up to the next integer. If the
-                 denominator is zero, no value is emitted."/>
-  </token>
-</histogram>
-
 <histogram name="PasswordManager.PasswordSyncState" enum="PasswordSyncState"
     expires_after="2022-06-12">
   <owner>kazinova@google.com</owner>
diff --git a/tools/metrics/histograms/metadata/phonehub/histograms.xml b/tools/metrics/histograms/metadata/phonehub/histograms.xml
index c232c1a8..5451b8e 100644
--- a/tools/metrics/histograms/metadata/phonehub/histograms.xml
+++ b/tools/metrics/histograms/metadata/phonehub/histograms.xml
@@ -102,6 +102,17 @@
   </summary>
 </histogram>
 
+<histogram name="PhoneHub.CameraRoll.OptInEvents"
+    enum="PhoneHubInterstitialScreenEvent" expires_after="2022-10-31">
+  <owner>jianbing@google.com</owner>
+  <owner>chromeos-cross-device-eng@google.com</owner>
+  <summary>
+    Records events from the Camera Roll opt-in view within the Phone Hub tray
+    bubble. Emitted when the opt-in view is shown and when the user clicks the
+    confirm/dismiss buttons.
+  </summary>
+</histogram>
+
 <histogram name="PhoneHub.CompletedUserAction" enum="PhoneHubUserAction"
     expires_after="2022-10-31">
   <owner>jonmann@chromium.org</owner>
diff --git a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
index b8018be76..96c8ebf 100644
--- a/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
+++ b/tools/metrics/histograms/metadata/segmentation_platform/histograms.xml
@@ -165,6 +165,19 @@
   </summary>
 </histogram>
 
+<histogram name="SegmentationPlatform.ModelAvailability.{SegmentationModel}"
+    enum="SegmentationModelAvailability" expires_after="2022-06-01">
+  <owner>ssid@chromium.org</owner>
+  <owner>chrome-segmentation-platform@google.com</owner>
+  <summary>
+    Records the availability of the segmentation models. Since there is no good
+    way to check if a model is available, this histogram records an entry when
+    model is requested and one when model is available. The difference should
+    show how often a model is not returned.
+  </summary>
+  <token key="SegmentationModel" variants="SegmentationModel"/>
+</histogram>
+
 <histogram
     name="SegmentationPlatform.ModelDelivery.HasMetadata.{SegmentationModel}"
     enum="BooleanYesNo" expires_after="2022-02-01">
@@ -384,6 +397,17 @@
   <token key="SegmentID" variants="SegmentationModel"/>
 </histogram>
 
+<histogram name="SegmentationPlatform.SelectionFailedReason"
+    enum="SegmentationSelectionFailureReason" expires_after="2022-06-01">
+  <owner>ssid@chromium.org</owner>
+  <owner>chrome-segmentation-platform@google.com</owner>
+  <summary>
+    Records the reason why the segmentation platform was unable to return a
+    segment selection to any client, or a result was available. Recorded when
+    failure is hit when trying to compute selection.
+  </summary>
+</histogram>
+
 <histogram
     name="SegmentationPlatform.SignalDatabase.GetSamples.DatabaseEntryCount"
     units="entries" expires_after="2022-04-24">
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 63c4d36..cec5f93 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -4401,6 +4401,50 @@
   </metric>
 </event>
 
+<event name="ChromeOSApp.InputEvent">
+  <owner>nancylingwang@chromium.org</owner>
+  <owner>dominickn@chromium.org</owner>
+  <summary>
+    Records each five minutes in Chrome OS to log the input events happened on
+    the app window. This is logged only when the user allows app syncing or
+    allows sync of everything, and the user does not set up a sync passphrase.
+  </summary>
+  <metric name="AppInputEventCount">
+    <summary>
+      The number of the input events happened in each five mintues.
+    </summary>
+  </metric>
+  <metric name="AppInputEventSource" enum="AppInputEventSource">
+    <summary>
+      An enum that records the input event source.
+    </summary>
+  </metric>
+  <metric name="AppType" enum="AppType">
+    <summary>
+      An enum that records the app type.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <enumeration/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+  <metric name="UserDeviceMatrix" enum="UserDeviceMatrix">
+    <summary>
+      An enum that records the user type and the device type.
+    </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <enumeration/>
+        </statistics>
+      </history>
+    </aggregation>
+  </metric>
+</event>
+
 <event name="ChromeOSApp.InstalledApp">
   <owner>nancylingwang@chromium.org</owner>
   <owner>dominickn@chromium.org</owner>
diff --git a/tools/perf/core/bot_platforms.py b/tools/perf/core/bot_platforms.py
index eb89dea4..2e118def 100644
--- a/tools/perf/core/bot_platforms.py
+++ b/tools/perf/core/bot_platforms.py
@@ -615,7 +615,7 @@
                                executables=_ANDROID_NEXUS_5_EXECUTABLE_CONFIGS)
 ANDROID_NEXUS_5X_WEBVIEW = PerfPlatform(
     'Android Nexus5X WebView Perf', 'Android AOSP MOB30K',
-    _ANDROID_NEXUS_5X_WEBVIEW_BENCHMARK_CONFIGS, 16, 'android')
+    _ANDROID_NEXUS_5X_WEBVIEW_BENCHMARK_CONFIGS, 10, 'android')
 ANDROID_PIXEL2 = PerfPlatform('android-pixel2-perf',
                               'Android OPM1.171019.021',
                               _ANDROID_PIXEL2_BENCHMARK_CONFIGS,
diff --git a/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json b/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
index 9246b912..f60c042 100644
--- a/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
+++ b/tools/perf/core/shard_maps/android_nexus5x_webview_perf_map.json
@@ -20,7 +20,7 @@
                 "abridged": false
             },
             "blink_perf.layout": {
-                "end": 7,
+                "end": 51,
                 "abridged": false
             }
         }
@@ -28,7 +28,7 @@
     "1": {
         "benchmarks": {
             "blink_perf.layout": {
-                "begin": 7,
+                "begin": 51,
                 "abridged": false
             },
             "blink_perf.owp_storage": {
@@ -38,15 +38,6 @@
                 "abridged": false
             },
             "blink_perf.parser": {
-                "end": 12,
-                "abridged": false
-            }
-        }
-    },
-    "2": {
-        "benchmarks": {
-            "blink_perf.parser": {
-                "begin": 12,
                 "abridged": false
             },
             "blink_perf.shadow_dom": {
@@ -58,6 +49,12 @@
             "blink_perf.webaudio": {
                 "abridged": false
             },
+            "blink_perf.webgl": {
+                "abridged": false
+            },
+            "blink_perf.webgl_fast_call": {
+                "abridged": false
+            },
             "dromaeo": {
                 "abridged": false
             },
@@ -74,15 +71,15 @@
                 "abridged": false
             },
             "loading.mobile": {
-                "end": 44,
+                "end": 26,
                 "abridged": false
             }
         }
     },
-    "3": {
+    "2": {
         "benchmarks": {
             "loading.mobile": {
-                "begin": 44,
+                "begin": 26,
                 "abridged": false
             },
             "media.mobile": {
@@ -92,19 +89,28 @@
                 "abridged": false
             },
             "rasterize_and_record_micro.top_25": {
-                "end": 5,
+                "abridged": false
+            },
+            "rendering.mobile": {
+                "end": 31,
+                "abridged": false
+            }
+        }
+    },
+    "3": {
+        "benchmarks": {
+            "rendering.mobile": {
+                "begin": 31,
+                "end": 163,
                 "abridged": false
             }
         }
     },
     "4": {
         "benchmarks": {
-            "rasterize_and_record_micro.top_25": {
-                "begin": 5,
-                "abridged": false
-            },
             "rendering.mobile": {
-                "end": 63,
+                "begin": 163,
+                "end": 270,
                 "abridged": false
             }
         }
@@ -112,43 +118,7 @@
     "5": {
         "benchmarks": {
             "rendering.mobile": {
-                "begin": 63,
-                "end": 149,
-                "abridged": false
-            }
-        }
-    },
-    "6": {
-        "benchmarks": {
-            "rendering.mobile": {
-                "begin": 149,
-                "end": 216,
-                "abridged": false
-            }
-        }
-    },
-    "7": {
-        "benchmarks": {
-            "rendering.mobile": {
-                "begin": 216,
-                "end": 321,
-                "abridged": false
-            }
-        }
-    },
-    "8": {
-        "benchmarks": {
-            "rendering.mobile": {
-                "begin": 321,
-                "end": 397,
-                "abridged": false
-            }
-        }
-    },
-    "9": {
-        "benchmarks": {
-            "rendering.mobile": {
-                "begin": 397,
+                "begin": 270,
                 "abridged": false
             },
             "speedometer": {
@@ -170,65 +140,45 @@
                 "abridged": false
             },
             "system_health.common_mobile": {
-                "end": 24,
+                "end": 2,
                 "abridged": false
             }
         }
     },
-    "10": {
+    "6": {
         "benchmarks": {
             "system_health.common_mobile": {
-                "begin": 24,
+                "begin": 2,
                 "abridged": false
             },
             "system_health.memory_mobile": {
-                "end": 6,
+                "end": 4,
                 "abridged": false
             }
         }
     },
-    "11": {
+    "7": {
         "benchmarks": {
-            "blink_perf.webgl": {
-                "abridged": false
-            },
-            "blink_perf.webgl_fast_call": {
-                "abridged": false
-            },
             "system_health.memory_mobile": {
-                "begin": 6,
-                "end": 22,
+                "begin": 4,
+                "end": 27,
                 "abridged": false
             }
         }
     },
-    "12": {
+    "8": {
         "benchmarks": {
             "system_health.memory_mobile": {
-                "begin": 22,
-                "end": 44,
-                "abridged": false
-            }
-        }
-    },
-    "13": {
-        "benchmarks": {
-            "system_health.memory_mobile": {
-                "begin": 44,
-                "end": 68,
-                "abridged": false
-            }
-        }
-    },
-    "14": {
-        "benchmarks": {
-            "system_health.memory_mobile": {
-                "begin": 68,
+                "begin": 27,
                 "abridged": false
             },
             "system_health.pcscan": {
                 "abridged": false
-            },
+            }
+        }
+    },
+    "9": {
+        "benchmarks": {
             "system_health.webview_startup": {
                 "abridged": false
             },
@@ -236,15 +186,6 @@
                 "abridged": false
             },
             "v8.browsing_mobile": {
-                "end": 5,
-                "abridged": false
-            }
-        }
-    },
-    "15": {
-        "benchmarks": {
-            "v8.browsing_mobile": {
-                "begin": 5,
                 "abridged": false
             },
             "wasmpspdfkit": {
@@ -256,26 +197,20 @@
         }
     },
     "extra_infos": {
-        "num_stories": 1107,
-        "predicted_min_shard_time": 1932.0,
-        "predicted_min_shard_index": 11,
-        "predicted_max_shard_time": 2076.0,
-        "predicted_max_shard_index": 13,
-        "shard #0": 1980.0,
-        "shard #1": 1978.0,
-        "shard #2": 1984.0,
-        "shard #3": 1987.0,
-        "shard #4": 2001.0,
-        "shard #5": 1986.0,
-        "shard #6": 1974.0,
-        "shard #7": 1982.0,
-        "shard #8": 1993.0,
-        "shard #9": 1993.0,
-        "shard #10": 1961.0,
-        "shard #11": 1932.0,
-        "shard #12": 1959.0,
-        "shard #13": 2076.0,
-        "shard #14": 1967.0,
-        "shard #15": 1978.0
+        "num_stories": 1161,
+        "predicted_min_shard_time": 2739.0,
+        "predicted_min_shard_index": 8,
+        "predicted_max_shard_time": 2897.0,
+        "predicted_max_shard_index": 9,
+        "shard #0": 2831.0,
+        "shard #1": 2843.0,
+        "shard #2": 2839.0,
+        "shard #3": 2823.0,
+        "shard #4": 2825.0,
+        "shard #5": 2828.0,
+        "shard #6": 2783.0,
+        "shard #7": 2865.0,
+        "shard #8": 2739.0,
+        "shard #9": 2897.0
     }
-}
+}
\ No newline at end of file
diff --git a/tools/perf/core/shard_maps/timing_data/android_nexus5x_webview_perf_timing.json b/tools/perf/core/shard_maps/timing_data/android_nexus5x_webview_perf_timing.json
index 88a264d..4e315e8 100644
--- a/tools/perf/core/shard_maps/timing_data/android_nexus5x_webview_perf_timing.json
+++ b/tools/perf/core/shard_maps/timing_data/android_nexus5x_webview_perf_timing.json
@@ -1,46 +1,62 @@
 [
     {
-        "duration": "2.0",
+        "duration": "3.0",
         "name": "blink_perf.accessibility/build-table.html"
     },
     {
-        "duration": "24.0",
+        "duration": "39.0",
         "name": "blink_perf.accessibility/focus-links.html"
     },
     {
-        "duration": "57.0",
+        "duration": "3.0",
         "name": "blink_perf.accessibility/line-breaks.html"
     },
     {
-        "duration": "33.0",
+        "duration": "3.0",
+        "name": "blink_perf.accessibility/many-nodes-toggle-content-visibility-auto.html"
+    },
+    {
+        "duration": "3.0",
+        "name": "blink_perf.accessibility/many-nodes-toggle-content-visibility-hidden.html"
+    },
+    {
+        "duration": "52.0",
+        "name": "blink_perf.accessibility/many-nodes-toggle-display-none.html"
+    },
+    {
+        "duration": "29.0",
         "name": "blink_perf.accessibility/many-text-changes-deep-block-subtree.html"
     },
     {
-        "duration": "33.0",
+        "duration": "27.0",
         "name": "blink_perf.accessibility/many-text-changes-deep-hidden-subtree.html"
     },
     {
-        "duration": "38.0",
+        "duration": "130.0",
         "name": "blink_perf.accessibility/many-text-changes-deep-inline-subtree.html"
     },
     {
-        "duration": "33.0",
+        "duration": "28.0",
         "name": "blink_perf.accessibility/many-text-changes-small-wait-between.html"
     },
     {
-        "duration": "18.0",
+        "duration": "20.0",
+        "name": "blink_perf.accessibility/slot-updates.html"
+    },
+    {
+        "duration": "19.0",
         "name": "blink_perf.accessibility/textarea-append.html"
     },
     {
-        "duration": "17.0",
+        "duration": "13.0",
         "name": "blink_perf.bindings/append-child.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/create-element.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/document-implementation.html"
     },
     {
@@ -48,31 +64,31 @@
         "name": "blink_perf.bindings/dom-attribute-on-prototoype.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/first-child.html"
     },
     {
-        "duration": "18.0",
+        "duration": "23.0",
         "name": "blink_perf.bindings/gc-forest.html"
     },
     {
-        "duration": "23.0",
+        "duration": "34.0",
         "name": "blink_perf.bindings/gc-mini-tree.html"
     },
     {
-        "duration": "49.0",
+        "duration": "74.0",
         "name": "blink_perf.bindings/gc-tree.html"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/get-attribute-rare.html"
     },
     {
-        "duration": "12.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/get-attribute.html"
     },
     {
-        "duration": "12.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/get-element-by-id.html"
     },
     {
@@ -84,7 +100,7 @@
         "name": "blink_perf.bindings/id-getter.html"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/id-setter.html"
     },
     {
@@ -92,7 +108,7 @@
         "name": "blink_perf.bindings/indexed-getter.html"
     },
     {
-        "duration": "13.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/insert-before.html"
     },
     {
@@ -100,11 +116,11 @@
         "name": "blink_perf.bindings/named-property-enumerator.html"
     },
     {
-        "duration": "61.0",
+        "duration": "144.0",
         "name": "blink_perf.bindings/node-list-access.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/node-type.html"
     },
     {
@@ -112,7 +128,7 @@
         "name": "blink_perf.bindings/post-message.html"
     },
     {
-        "duration": "13.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/sequence-conversion-array.html"
     },
     {
@@ -128,7 +144,7 @@
         "name": "blink_perf.bindings/serialize-long-string.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/serialize-map.html"
     },
     {
@@ -136,11 +152,11 @@
         "name": "blink_perf.bindings/serialize-nested-array.html"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/set-attribute-rare.html"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/set-attribute.html"
     },
     {
@@ -160,7 +176,7 @@
         "name": "blink_perf.bindings/structured-clone-long-string-serialize.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/typed-array-construct-from-array.html"
     },
     {
@@ -172,19 +188,19 @@
         "name": "blink_perf.bindings/typed-array-construct-from-typed.html"
     },
     {
-        "duration": "12.0",
+        "duration": "10.0",
         "name": "blink_perf.bindings/typed-array-set-from-typed.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/undefined-first-child.html"
     },
     {
-        "duration": "16.0",
+        "duration": "12.0",
         "name": "blink_perf.bindings/undefined-get-element-by-id.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.bindings/undefined-id-getter.html"
     },
     {
@@ -204,7 +220,7 @@
         "name": "blink_perf.bindings/worker-structured-clone-json-to-worker.html"
     },
     {
-        "duration": "9.0",
+        "duration": "8.0",
         "name": "blink_perf.bindings/worker-structured-clone-workerDOM-DBMon-from-worker.html"
     },
     {
@@ -212,35 +228,35 @@
         "name": "blink_perf.bindings/worker-structured-clone-workerDOM-Map-from-worker.html"
     },
     {
-        "duration": "20.0",
+        "duration": "23.0",
         "name": "blink_perf.bindings/worker-text-encoded-transferable-from-worker.html"
     },
     {
-        "duration": "20.0",
+        "duration": "22.0",
         "name": "blink_perf.bindings/worker-text-encoded-transferable-roundtrip.html"
     },
     {
-        "duration": "20.0",
+        "duration": "23.0",
         "name": "blink_perf.bindings/worker-text-encoded-transferable-to-worker.html"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "blink_perf.bindings/worker-transferable-from-worker.html"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "blink_perf.bindings/worker-transferable-roundtrip.html"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "blink_perf.bindings/worker-transferable-to-worker.html"
     },
     {
-        "duration": "27.0",
+        "duration": "26.0",
         "name": "blink_perf.css/AttributeDescendantSelector.html"
     },
     {
-        "duration": "9.0",
+        "duration": "8.0",
         "name": "blink_perf.css/CSSLogicalDirection.html"
     },
     {
@@ -248,95 +264,95 @@
         "name": "blink_perf.css/CSSPropertySetterGetter.html"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "blink_perf.css/CSSPropertySetterGetterMethods.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.css/CSSPropertyUpdateValue.html"
     },
     {
-        "duration": "16.0",
+        "duration": "17.0",
         "name": "blink_perf.css/ChangeStyleCSSVariableRecalc.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleChildClassSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleChildElementSelectors.html"
     },
     {
-        "duration": "16.0",
+        "duration": "22.0",
         "name": "blink_perf.css/ChangeStyleCustomPropertyDeclaration.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleElementSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleGrandChildElementSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleMultipleClassSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleMultipleQualifiedDataAttributesWithValuesSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleNestedPseudoSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStylePairOfNthChildSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStylePartialAttributeMatchingSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleQualifiedDataAttributeSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleQualifiedDataAttributeWithValueSelector.html"
     },
     {
-        "duration": "9.0",
+        "duration": "8.0",
         "name": "blink_perf.css/ChangeStyleShallowTree.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleSingleClassSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleSingleNthChildSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleSinglePseudoSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleUniversalSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleUnqualifiedDataAttributeSelector.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.css/ChangeStyleUnqualifiedDataAttributeWithValueSelector.html"
     },
     {
-        "duration": "26.0",
+        "duration": "25.0",
         "name": "blink_perf.css/ClassDescendantSelector.html"
     },
     {
@@ -344,11 +360,11 @@
         "name": "blink_perf.css/ClassInvalidation.html"
     },
     {
-        "duration": "8.0",
+        "duration": "6.0",
         "name": "blink_perf.css/CustomPropertiesCascade.html"
     },
     {
-        "duration": "11.0",
+        "duration": "12.0",
         "name": "blink_perf.css/CustomPropertiesDependency.html"
     },
     {
@@ -356,7 +372,7 @@
         "name": "blink_perf.css/CustomPropertiesNonRootInheritance.html"
     },
     {
-        "duration": "12.0",
+        "duration": "6.0",
         "name": "blink_perf.css/CustomPropertiesPendingSubstitution.html"
     },
     {
@@ -364,7 +380,7 @@
         "name": "blink_perf.css/CustomPropertiesRootInheritance.html"
     },
     {
-        "duration": "2.0",
+        "duration": "1.0",
         "name": "blink_perf.css/CustomPropertiesVarAlias.html"
     },
     {
@@ -372,59 +388,71 @@
         "name": "blink_perf.css/ExplicitInheritance.html"
     },
     {
-        "duration": "21.0",
+        "duration": "19.0",
         "name": "blink_perf.css/FocusUpdate.html"
     },
     {
-        "duration": "8.0",
+        "duration": "7.0",
+        "name": "blink_perf.css/HighlightInheritanceRecalc.html"
+    },
+    {
+        "duration": "7.0",
         "name": "blink_perf.css/LoadBootstrapBlog.html"
     },
     {
-        "duration": "9.0",
+        "duration": "7.0",
         "name": "blink_perf.css/LoadMaterializeStarterPage.html"
     },
     {
-        "duration": "10.0",
+        "duration": "8.0",
         "name": "blink_perf.css/LoadSemanticPageExample.html"
     },
     {
-        "duration": "14.0",
+        "duration": "13.0",
         "name": "blink_perf.css/PseudoClassSelectors.html"
     },
     {
-        "duration": "66.0",
+        "duration": "50.0",
         "name": "blink_perf.css/SelectorCountScaling.html"
     },
     {
-        "duration": "18.0",
+        "duration": "14.0",
         "name": "blink_perf.dom/custom-element-default-style-with-shadow.html"
     },
     {
-        "duration": "11.0",
+        "duration": "13.0",
         "name": "blink_perf.dom/custom-element-default-style.html"
     },
     {
-        "duration": "28.0",
+        "duration": "26.0",
+        "name": "blink_perf.dom/insert-text-with-dir-auto.html"
+    },
+    {
+        "duration": "30.0",
         "name": "blink_perf.dom/long-sibling-list.html"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "blink_perf.dom/modify-element-classname.html"
     },
     {
-        "duration": "11.0",
+        "duration": "14.0",
         "name": "blink_perf.dom/modify-element-id.html"
     },
     {
-        "duration": "11.0",
+        "duration": "8.0",
         "name": "blink_perf.dom/modify-element-title.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
+        "name": "blink_perf.dom/replace-text-with-dir-auto.html"
+    },
+    {
+        "duration": "11.0",
         "name": "blink_perf.dom/select-multiple-add.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.dom/select-single-add.html"
     },
     {
@@ -432,7 +460,7 @@
         "name": "blink_perf.dom/select-single-remove.html"
     },
     {
-        "duration": "29.0",
+        "duration": "14.0",
         "name": "blink_perf.events/EventsDispatching.html"
     },
     {
@@ -444,75 +472,75 @@
         "name": "blink_perf.events/EventsDispatchingInV1ShadowTrees.html"
     },
     {
-        "duration": "54.0",
+        "duration": "69.0",
         "name": "blink_perf.events/hit-test-lots-of-layers.html"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "blink_perf.events/is-input-pending-default-events.html"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "blink_perf.events/is-input-pending-include-continuous-events.html"
     },
     {
-        "duration": "46.0",
+        "duration": "29.0",
         "name": "blink_perf.image_decoder/decode-gif.html"
     },
     {
-        "duration": "19.0",
+        "duration": "16.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h1v1.html"
     },
     {
-        "duration": "18.0",
+        "duration": "15.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h1v2.html"
     },
     {
-        "duration": "18.0",
+        "duration": "15.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h2v1.html"
     },
     {
-        "duration": "17.0",
+        "duration": "15.0",
         "name": "blink_perf.image_decoder/decode-jpeg-h2v2.html"
     },
     {
-        "duration": "49.0",
+        "duration": "40.0",
         "name": "blink_perf.image_decoder/decode-lossless-webp.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "blink_perf.image_decoder/decode-lossy-webp.html"
     },
     {
-        "duration": "36.0",
+        "duration": "26.0",
         "name": "blink_perf.image_decoder/decode-png-palette-opaque.html"
     },
     {
-        "duration": "19.0",
+        "duration": "16.0",
         "name": "blink_perf.image_decoder/decode-png-palette.html"
     },
     {
-        "duration": "53.0",
+        "duration": "43.0",
         "name": "blink_perf.image_decoder/decode-png.html"
     },
     {
-        "duration": "15.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/ArabicLineLayout.html"
     },
     {
-        "duration": "7.0",
+        "duration": "5.0",
         "name": "blink_perf.layout/Shapes/MultipleShapes.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/SimpleTextPathLineLayout.html"
     },
     {
-        "duration": "48.0",
+        "duration": "59.0",
         "name": "blink_perf.layout/abspos.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/add-remove-inline-floats.html"
     },
     {
@@ -520,15 +548,15 @@
         "name": "blink_perf.layout/animate-abspos-deep.html"
     },
     {
-        "duration": "13.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/attach-inlines-2.html"
     },
     {
-        "duration": "12.0",
+        "duration": "19.0",
         "name": "blink_perf.layout/attach-inlines.html"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "blink_perf.layout/auto-grid-lots-of-data.html"
     },
     {
@@ -556,15 +584,27 @@
         "name": "blink_perf.layout/chapter-reflow.html"
     },
     {
-        "duration": "11.0",
+        "duration": "7.0",
         "name": "blink_perf.layout/character_fallback.html"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "blink_perf.layout/contain-content-style-change.html"
     },
     {
-        "duration": "9.0",
+        "duration": "23.0",
+        "name": "blink_perf.layout/css-contain-change-text-different-subtree-root.html"
+    },
+    {
+        "duration": "21.0",
+        "name": "blink_perf.layout/css-contain-change-text-without-subtree-root.html"
+    },
+    {
+        "duration": "23.0",
+        "name": "blink_perf.layout/css-contain-change-text.html"
+    },
+    {
+        "duration": "11.0",
         "name": "blink_perf.layout/culled-inline-bounding-rects.html"
     },
     {
@@ -596,7 +636,7 @@
         "name": "blink_perf.layout/editing_prepend.html"
     },
     {
-        "duration": "26.0",
+        "duration": "36.0",
         "name": "blink_perf.layout/fit-content-change-available-size-blocks.html"
     },
     {
@@ -604,11 +644,11 @@
         "name": "blink_perf.layout/fit-content-change-available-size-text.html"
     },
     {
-        "duration": "15.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/fixed-grid-lots-of-data.html"
     },
     {
-        "duration": "15.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/fixed-grid-lots-of-stretched-data.html"
     },
     {
@@ -628,19 +668,23 @@
         "name": "blink_perf.layout/flexbox-hittest.html"
     },
     {
-        "duration": "13.0",
+        "duration": "14.0",
         "name": "blink_perf.layout/flexbox-input.html"
     },
     {
-        "duration": "14.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/flexbox-lots-of-data.html"
     },
     {
         "duration": "12.0",
+        "name": "blink_perf.layout/flexbox-nested-rows-and-columns-auto-overflow.html"
+    },
+    {
+        "duration": "11.0",
         "name": "blink_perf.layout/flexbox-row-nowrap.html"
     },
     {
-        "duration": "19.0",
+        "duration": "24.0",
         "name": "blink_perf.layout/flexbox-row-stretch-height-definite.html"
     },
     {
@@ -656,91 +700,103 @@
         "name": "blink_perf.layout/flexbox-with-stretch-layout.html"
     },
     {
-        "duration": "20.0",
+        "duration": "21.0",
         "name": "blink_perf.layout/flexbox_with_list_item.html"
     },
     {
-        "duration": "39.0",
+        "duration": "49.0",
         "name": "blink_perf.layout/floats_100_100.html"
     },
     {
-        "duration": "39.0",
+        "duration": "49.0",
         "name": "blink_perf.layout/floats_100_100_nested.html"
     },
     {
-        "duration": "21.0",
+        "duration": "28.0",
         "name": "blink_perf.layout/floats_10_1000.html"
     },
     {
-        "duration": "14.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/floats_20_100.html"
     },
     {
-        "duration": "14.0",
+        "duration": "17.0",
         "name": "blink_perf.layout/floats_20_100_nested.html"
     },
     {
-        "duration": "11.0",
+        "duration": "15.0",
         "name": "blink_perf.layout/floats_2_100.html"
     },
     {
-        "duration": "8.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/floats_2_100_nested.html"
     },
     {
-        "duration": "14.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/floats_50_100.html"
     },
     {
-        "duration": "14.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/floats_50_100_nested.html"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "blink_perf.layout/floats_show_hide.html"
     },
     {
-        "duration": "15.0",
-        "name": "blink_perf.layout/hindi-line-layout.html"
-    },
-    {
-        "duration": "10.0",
-        "name": "blink_perf.layout/hittest-block-children.html"
-    },
-    {
-        "duration": "50.0",
-        "name": "blink_perf.layout/japanese-kokoro-insert.html"
-    },
-    {
-        "duration": "27.0",
-        "name": "blink_perf.layout/large-grid.html"
+        "duration": "12.0",
+        "name": "blink_perf.layout/grid-nested-baseline.html"
     },
     {
         "duration": "12.0",
+        "name": "blink_perf.layout/grid-with-block-constraints-dependence.html"
+    },
+    {
+        "duration": "11.0",
+        "name": "blink_perf.layout/hindi-line-layout.html"
+    },
+    {
+        "duration": "12.0",
+        "name": "blink_perf.layout/hittest-block-children.html"
+    },
+    {
+        "duration": "13.0",
+        "name": "blink_perf.layout/hittest-nested-inline-blocks-listbased.html"
+    },
+    {
+        "duration": "22.0",
+        "name": "blink_perf.layout/japanese-kokoro-insert.html"
+    },
+    {
+        "duration": "31.0",
+        "name": "blink_perf.layout/large-grid.html"
+    },
+    {
+        "duration": "13.0",
         "name": "blink_perf.layout/large-spanning-grid-item.html"
     },
     {
-        "duration": "38.0",
+        "duration": "47.0",
         "name": "blink_perf.layout/large-table-with-collapsed-borders-and-colspans-wider-than-table.html"
     },
     {
-        "duration": "37.0",
+        "duration": "46.0",
         "name": "blink_perf.layout/large-table-with-collapsed-borders-and-colspans.html"
     },
     {
-        "duration": "37.0",
+        "duration": "46.0",
         "name": "blink_perf.layout/large-table-with-collapsed-borders-and-no-colspans.html"
     },
     {
-        "duration": "30.0",
+        "duration": "34.0",
         "name": "blink_perf.layout/latin-ebook-resize.html"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "blink_perf.layout/latin-ebook.html"
     },
     {
-        "duration": "9.0",
+        "duration": "10.0",
         "name": "blink_perf.layout/layers_overlap_2d.html"
     },
     {
@@ -756,15 +812,15 @@
         "name": "blink_perf.layout/line-layout-fit-content.html"
     },
     {
-        "duration": "36.0",
+        "duration": "44.0",
         "name": "blink_perf.layout/line-layout-line-height.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/line-layout-repeat-append-select.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/line-layout-repeat-append.html"
     },
     {
@@ -780,15 +836,15 @@
         "name": "blink_perf.layout/long-line-nowrap-spans-collapse.html"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "blink_perf.layout/long-line-nowrap.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/many-block-children-auto-inline-size.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/many-block-children-fixed-inline-size.html"
     },
     {
@@ -808,23 +864,27 @@
         "name": "blink_perf.layout/multicol/lots-of-small-nested-unbreakable-blocks-autofill.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/multicol/lots-of-small-unbreakable-blocks-autofill.html"
     },
     {
-        "duration": "11.0",
+        "duration": "12.0",
+        "name": "blink_perf.layout/multicol/lots-of-small-unbreakable-blocks-balanced.html"
+    },
+    {
+        "duration": "12.0",
         "name": "blink_perf.layout/multicol/lots-of-text-autofill.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/multicol/lots-of-text-balanced-orphans-widows.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/multicol/lots-of-text-balanced.html"
     },
     {
-        "duration": "86.0",
+        "duration": "99.0",
         "name": "blink_perf.layout/multicol/nested-forced-breaks.html"
     },
     {
@@ -836,22 +896,26 @@
         "name": "blink_perf.layout/multicol/tall-content-short-columns.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.layout/nested-blocks-with-percent-height-and-max-height.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.layout/nested-grid-lots-of-tracks.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/nested-grid.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.layout/nested-percent-height-tables.html"
     },
     {
+        "duration": "11.0",
+        "name": "blink_perf.layout/nested-tables-with-overflow-auto.html"
+    },
+    {
         "duration": "13.0",
         "name": "blink_perf.layout/ruby.html"
     },
@@ -860,7 +924,7 @@
         "name": "blink_perf.layout/subtree-detaching.html"
     },
     {
-        "duration": "52.0",
+        "duration": "24.0",
         "name": "blink_perf.layout/vertical-japanese-kokoro-insert.html"
     },
     {
@@ -876,19 +940,19 @@
         "name": "blink_perf.layout/word-wrap-break-word.html"
     },
     {
-        "duration": "35.0",
+        "duration": "15.0",
         "name": "blink_perf.owp_storage/blob-perf-files.html"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "blink_perf.owp_storage/blob-perf-ipc.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.owp_storage/blob-perf-shm.html"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "blink_perf.owp_storage/blob-perf-tiny.html"
     },
     {
@@ -948,39 +1012,39 @@
         "name": "blink_perf.paint/transform-changes.html"
     },
     {
-        "duration": "16.0",
+        "duration": "14.0",
         "name": "blink_perf.parser/css-parser-yui.html"
     },
     {
-        "duration": "25.0",
+        "duration": "18.0",
         "name": "blink_perf.parser/declarative-shadow-dom-cloning.html"
     },
     {
-        "duration": "15.0",
+        "duration": "12.0",
         "name": "blink_perf.parser/declarative-shadow-dom.html"
     },
     {
-        "duration": "16.0",
+        "duration": "22.0",
         "name": "blink_perf.parser/html-parser-threaded.html"
     },
     {
-        "duration": "50.0",
+        "duration": "24.0",
         "name": "blink_perf.parser/html-parser.html"
     },
     {
-        "duration": "176.0",
+        "duration": "151.0",
         "name": "blink_perf.parser/html5-full-render.html"
     },
     {
-        "duration": "25.0",
+        "duration": "20.0",
         "name": "blink_perf.parser/iframe-append-remove.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "blink_perf.parser/innerHTML-setter-siblings.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/innerHTML-setter.html"
     },
     {
@@ -996,7 +1060,7 @@
         "name": "blink_perf.parser/query-selector-all-class-deep.html"
     },
     {
-        "duration": "12.0",
+        "duration": "21.0",
         "name": "blink_perf.parser/query-selector-all-class-first.html"
     },
     {
@@ -1004,7 +1068,7 @@
         "name": "blink_perf.parser/query-selector-all-class-last.html"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/query-selector-all-class.html"
     },
     {
@@ -1028,7 +1092,7 @@
         "name": "blink_perf.parser/query-selector-all-id-last.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/query-selector-all-last.html"
     },
     {
@@ -1036,7 +1100,7 @@
         "name": "blink_perf.parser/query-selector-deep.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "blink_perf.parser/query-selector-first.html"
     },
     {
@@ -1052,19 +1116,19 @@
         "name": "blink_perf.parser/query-selector-last.html"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "blink_perf.parser/simple-url.html"
     },
     {
-        "duration": "12.0",
+        "duration": "13.0",
         "name": "blink_perf.parser/textarea-parsing.html"
     },
     {
-        "duration": "15.0",
+        "duration": "20.0",
         "name": "blink_perf.parser/tiny-innerHTML.html"
     },
     {
-        "duration": "16.0",
+        "duration": "14.0",
         "name": "blink_perf.parser/url-parser.html"
     },
     {
@@ -1117,6 +1181,18 @@
     },
     {
         "duration": "0.0",
+        "name": "blink_perf.shadow_dom/imperative-shadow-dom-overhead.html"
+    },
+    {
+        "duration": "0.0",
+        "name": "blink_perf.shadow_dom/shadow-dom-overhead-iframe.html"
+    },
+    {
+        "duration": "0.0",
+        "name": "blink_perf.shadow_dom/shadow-dom-overhead.html"
+    },
+    {
+        "duration": "0.0",
         "name": "blink_perf.shadow_dom/shadow-style-share-attr-selectors.html"
     },
     {
@@ -1216,7 +1292,7 @@
         "name": "blink_perf.shadow_dom/v1-small-shallow-layout.html"
     },
     {
-        "duration": "22.0",
+        "duration": "8.0",
         "name": "blink_perf.svg/AzLizardBenjiPark.html"
     },
     {
@@ -1224,11 +1300,11 @@
         "name": "blink_perf.svg/Bamboo.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.svg/Cactus.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.svg/Cowboy.html"
     },
     {
@@ -1244,7 +1320,7 @@
         "name": "blink_perf.svg/Debian.html"
     },
     {
-        "duration": "6.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/DropsOnABlade.html"
     },
     {
@@ -1252,19 +1328,19 @@
         "name": "blink_perf.svg/FlowerFromMyGarden.html"
     },
     {
-        "duration": "6.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/FoodLeifLodahl.html"
     },
     {
-        "duration": "6.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/France.html"
     },
     {
-        "duration": "6.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/FrancoBolloGnomeEzechi.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.svg/GearFlowers.html"
     },
     {
@@ -1272,15 +1348,15 @@
         "name": "blink_perf.svg/HarveyRayner.html"
     },
     {
-        "duration": "6.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/HereGear.html"
     },
     {
-        "duration": "14.0",
+        "duration": "16.0",
         "name": "blink_perf.svg/MtSaintHelens.html"
     },
     {
-        "duration": "6.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/Samurai.html"
     },
     {
@@ -1292,7 +1368,7 @@
         "name": "blink_perf.svg/SvgCubics.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.svg/SvgHitTesting.html"
     },
     {
@@ -1300,11 +1376,11 @@
         "name": "blink_perf.svg/SvgNestedUse.html"
     },
     {
-        "duration": "6.0",
+        "duration": "5.0",
         "name": "blink_perf.svg/UnderTheSee.html"
     },
     {
-        "duration": "7.0",
+        "duration": "6.0",
         "name": "blink_perf.svg/WorldIso.html"
     },
     {
@@ -1312,35 +1388,75 @@
         "name": "blink_perf.svg/Worldcup.html"
     },
     {
-        "duration": "20.0",
+        "duration": "15.0",
         "name": "blink_perf.webaudio/audio-buffer-source-node.html"
     },
     {
-        "duration": "122.0",
+        "duration": "93.0",
         "name": "blink_perf.webaudio/audio-worklet-node.html"
     },
     {
-        "duration": "119.0",
+        "duration": "89.0",
         "name": "blink_perf.webaudio/biquad-filter-node.html"
     },
     {
-        "duration": "118.0",
+        "duration": "112.0",
+        "name": "blink_perf.webaudio/dynamics-compressor-node.html"
+    },
+    {
+        "duration": "88.0",
         "name": "blink_perf.webaudio/gain-node.html"
     },
     {
-        "duration": "59.0",
+        "duration": "45.0",
         "name": "blink_perf.webaudio/panner-node.html"
     },
     {
-        "duration": "40.0",
-        "name": "dromaeo/http://dromaeo.com?dom-attr"
+        "duration": "19.0",
+        "name": "blink_perf.webaudio/timeline-insert-event.html"
+    },
+    {
+        "duration": "13.0",
+        "name": "blink_perf.webgl/binding-bind-buffer.html"
+    },
+    {
+        "duration": "6.0",
+        "name": "blink_perf.webgl/binding-buffer-sub-data.html"
+    },
+    {
+        "duration": "7.0",
+        "name": "blink_perf.webgl/binding-draw-arrays.html"
+    },
+    {
+        "duration": "9.0",
+        "name": "blink_perf.webgl/binding-typed-array-uniforms.html"
+    },
+    {
+        "duration": "11.0",
+        "name": "blink_perf.webgl_fast_call/binding-bind-buffer.html"
+    },
+    {
+        "duration": "6.0",
+        "name": "blink_perf.webgl_fast_call/binding-buffer-sub-data.html"
+    },
+    {
+        "duration": "7.0",
+        "name": "blink_perf.webgl_fast_call/binding-draw-arrays.html"
+    },
+    {
+        "duration": "8.0",
+        "name": "blink_perf.webgl_fast_call/binding-typed-array-uniforms.html"
     },
     {
         "duration": "37.0",
+        "name": "dromaeo/http://dromaeo.com?dom-attr"
+    },
+    {
+        "duration": "36.0",
         "name": "dromaeo/http://dromaeo.com?dom-modify"
     },
     {
-        "duration": "53.0",
+        "duration": "52.0",
         "name": "dromaeo/http://dromaeo.com?dom-query"
     },
     {
@@ -1348,11 +1464,11 @@
         "name": "dromaeo/http://dromaeo.com?dom-traverse"
     },
     {
-        "duration": "10.0",
+        "duration": "5.0",
         "name": "dummy_benchmark.noisy_benchmark_1/dummy_page.html"
     },
     {
-        "duration": "7.0",
+        "duration": "5.0",
         "name": "dummy_benchmark.stable_benchmark_1/dummy_page.html"
     },
     {
@@ -1360,51 +1476,51 @@
         "name": "jetstream/JetStream"
     },
     {
-        "duration": "79.0",
+        "duration": "70.0",
         "name": "kraken/http://krakenbenchmark.mozilla.org/kraken-1.1/driver.html"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "loading.mobile/58Pic"
     },
     {
-        "duration": "27.0",
+        "duration": "19.0",
         "name": "loading.mobile/58Pic_3g"
     },
     {
-        "duration": "14.0",
+        "duration": "13.0",
         "name": "loading.mobile/Amazon"
     },
     {
-        "duration": "29.0",
+        "duration": "27.0",
         "name": "loading.mobile/Amazon_3g"
     },
     {
-        "duration": "15.0",
+        "duration": "13.0",
         "name": "loading.mobile/BOLNoticias"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "loading.mobile/BOLNoticias_3g"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "loading.mobile/Baidu"
     },
     {
-        "duration": "20.0",
+        "duration": "19.0",
         "name": "loading.mobile/Baidu_3g"
     },
     {
-        "duration": "17.0",
+        "duration": "12.0",
         "name": "loading.mobile/Bradesco"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/Bradesco_3g"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "loading.mobile/Dailymotion"
     },
     {
@@ -1412,55 +1528,55 @@
         "name": "loading.mobile/Dailymotion_3g"
     },
     {
-        "duration": "19.0",
+        "duration": "15.0",
         "name": "loading.mobile/Dawn"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/Dawn_3g"
     },
     {
-        "duration": "15.0",
+        "duration": "10.0",
         "name": "loading.mobile/DevOpera_cold"
     },
     {
-        "duration": "21.0",
+        "duration": "14.0",
         "name": "loading.mobile/DevOpera_cold_3g"
     },
     {
-        "duration": "21.0",
+        "duration": "16.0",
         "name": "loading.mobile/DevOpera_hot"
     },
     {
-        "duration": "25.0",
+        "duration": "19.0",
         "name": "loading.mobile/DevOpera_hot_3g"
     },
     {
-        "duration": "20.0",
+        "duration": "13.0",
         "name": "loading.mobile/DevOpera_warm"
     },
     {
-        "duration": "22.0",
+        "duration": "16.0",
         "name": "loading.mobile/DevOpera_warm_3g"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/Dramaq"
     },
     {
-        "duration": "53.0",
+        "duration": "46.0",
         "name": "loading.mobile/Dramaq_3g"
     },
     {
-        "duration": "14.0",
+        "duration": "9.0",
         "name": "loading.mobile/EnquiryIndianRail"
     },
     {
-        "duration": "18.0",
+        "duration": "12.0",
         "name": "loading.mobile/EnquiryIndianRail_3g"
     },
     {
-        "duration": "10.0",
+        "duration": "9.0",
         "name": "loading.mobile/Facebook"
     },
     {
@@ -1468,59 +1584,59 @@
         "name": "loading.mobile/Facebook_3g"
     },
     {
-        "duration": "17.0",
+        "duration": "11.0",
         "name": "loading.mobile/FlipBoard_cold"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/FlipBoard_cold_3g"
     },
     {
-        "duration": "24.0",
+        "duration": "17.0",
         "name": "loading.mobile/FlipBoard_hot"
     },
     {
-        "duration": "25.0",
+        "duration": "18.0",
         "name": "loading.mobile/FlipBoard_hot_3g"
     },
     {
-        "duration": "20.0",
+        "duration": "14.0",
         "name": "loading.mobile/FlipBoard_warm"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/FlipBoard_warm_3g"
     },
     {
-        "duration": "20.0",
+        "duration": "13.0",
         "name": "loading.mobile/FlipKart_cold"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/FlipKart_cold_3g"
     },
     {
-        "duration": "26.0",
+        "duration": "20.0",
         "name": "loading.mobile/FlipKart_hot"
     },
     {
-        "duration": "31.0",
+        "duration": "20.0",
         "name": "loading.mobile/FlipKart_hot_3g"
     },
     {
-        "duration": "21.0",
+        "duration": "17.0",
         "name": "loading.mobile/FlipKart_warm"
     },
     {
-        "duration": "37.0",
+        "duration": "28.0",
         "name": "loading.mobile/FlipKart_warm_3g"
     },
     {
-        "duration": "16.0",
+        "duration": "11.0",
         "name": "loading.mobile/FranceTVInfo"
     },
     {
-        "duration": "23.0",
+        "duration": "18.0",
         "name": "loading.mobile/FranceTVInfo_3g"
     },
     {
@@ -1532,15 +1648,19 @@
         "name": "loading.mobile/G1_3g"
     },
     {
-        "duration": "19.0",
+        "duration": "14.0",
         "name": "loading.mobile/GSShop"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/GSShop_3g"
     },
     {
-        "duration": "10.0",
+        "duration": "13.0",
+        "name": "loading.mobile/Google"
+    },
+    {
+        "duration": "9.0",
         "name": "loading.mobile/GoogleBrazil"
     },
     {
@@ -1548,7 +1668,7 @@
         "name": "loading.mobile/GoogleBrazil_3g"
     },
     {
-        "duration": "11.0",
+        "duration": "10.0",
         "name": "loading.mobile/GoogleIndia"
     },
     {
@@ -1556,7 +1676,7 @@
         "name": "loading.mobile/GoogleIndia_3g"
     },
     {
-        "duration": "10.0",
+        "duration": "9.0",
         "name": "loading.mobile/GoogleIndonesia"
     },
     {
@@ -1564,7 +1684,7 @@
         "name": "loading.mobile/GoogleIndonesia_3g"
     },
     {
-        "duration": "13.0",
+        "duration": "11.0",
         "name": "loading.mobile/GoogleRedirectToGoogleJapan"
     },
     {
@@ -1572,15 +1692,19 @@
         "name": "loading.mobile/GoogleRedirectToGoogleJapan_3g"
     },
     {
-        "duration": "6.0",
+        "duration": "26.0",
+        "name": "loading.mobile/Google_3g"
+    },
+    {
+        "duration": "2.0",
         "name": "loading.mobile/Hongkiat"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/Hongkiat_3g"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "loading.mobile/KapanLagi"
     },
     {
@@ -1588,7 +1712,7 @@
         "name": "loading.mobile/KapanLagi_3g"
     },
     {
-        "duration": "16.0",
+        "duration": "14.0",
         "name": "loading.mobile/Kaskus"
     },
     {
@@ -1596,27 +1720,27 @@
         "name": "loading.mobile/Kaskus_3g"
     },
     {
-        "duration": "16.0",
+        "duration": "13.0",
         "name": "loading.mobile/LocalMoxie"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/LocalMoxie_3g"
     },
     {
-        "duration": "15.0",
+        "duration": "9.0",
         "name": "loading.mobile/Locanto"
     },
     {
-        "duration": "16.0",
+        "duration": "11.0",
         "name": "loading.mobile/Locanto_3g"
     },
     {
-        "duration": "19.0",
+        "duration": "11.0",
         "name": "loading.mobile/OLX"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/OLX_3g"
     },
     {
@@ -1628,55 +1752,55 @@
         "name": "loading.mobile/QQNews_3g"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "loading.mobile/SlideShare"
     },
     {
-        "duration": "28.0",
+        "duration": "21.0",
         "name": "loading.mobile/SlideShare_3g"
     },
     {
-        "duration": "22.0",
+        "duration": "9.0",
         "name": "loading.mobile/Suumo_cold"
     },
     {
-        "duration": "18.0",
+        "duration": "10.0",
         "name": "loading.mobile/Suumo_cold_3g"
     },
     {
-        "duration": "19.0",
+        "duration": "14.0",
         "name": "loading.mobile/Suumo_hot"
     },
     {
-        "duration": "19.0",
+        "duration": "14.0",
         "name": "loading.mobile/Suumo_hot_3g"
     },
     {
-        "duration": "17.0",
+        "duration": "11.0",
         "name": "loading.mobile/Suumo_warm"
     },
     {
-        "duration": "17.0",
+        "duration": "11.0",
         "name": "loading.mobile/Suumo_warm_3g"
     },
     {
-        "duration": "16.0",
+        "duration": "12.0",
         "name": "loading.mobile/Thairath"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/Thairath_3g"
     },
     {
-        "duration": "23.0",
+        "duration": "16.0",
         "name": "loading.mobile/TheStar"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/TheStar_3g"
     },
     {
-        "duration": "15.0",
+        "duration": "13.0",
         "name": "loading.mobile/TribunNews"
     },
     {
@@ -1684,35 +1808,35 @@
         "name": "loading.mobile/TribunNews_3g"
     },
     {
-        "duration": "10.0",
+        "duration": "9.0",
         "name": "loading.mobile/Twitter"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "loading.mobile/Twitter_3g"
     },
     {
-        "duration": "16.0",
+        "duration": "10.0",
         "name": "loading.mobile/VoiceMemos_cold"
     },
     {
-        "duration": "19.0",
+        "duration": "13.0",
         "name": "loading.mobile/VoiceMemos_cold_3g"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/VoiceMemos_hot"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/VoiceMemos_hot_3g"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/VoiceMemos_warm"
     },
     {
-        "duration": "6.0",
+        "duration": "2.0",
         "name": "loading.mobile/VoiceMemos_warm_3g"
     },
     {
@@ -1720,11 +1844,11 @@
         "name": "loading.mobile/Wikipedia"
     },
     {
-        "duration": "15.0",
+        "duration": "13.0",
         "name": "loading.mobile/Wikipedia_3g"
     },
     {
-        "duration": "15.0",
+        "duration": "14.0",
         "name": "loading.mobile/YahooNews"
     },
     {
@@ -1740,79 +1864,79 @@
         "name": "loading.mobile/Youtube_3g"
     },
     {
-        "duration": "73.0",
+        "duration": "64.0",
         "name": "octane/Octane"
     },
     {
-        "duration": "31.0",
+        "duration": "21.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/amazon.html"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/blogger.html"
     },
     {
-        "duration": "31.0",
+        "duration": "19.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/booking.html"
     },
     {
-        "duration": "17.0",
+        "duration": "14.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/cnn.html"
     },
     {
-        "duration": "19.0",
+        "duration": "12.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/ebay.html"
     },
     {
-        "duration": "30.0",
+        "duration": "28.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/espn.html"
     },
     {
-        "duration": "27.0",
+        "duration": "13.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/facebook.html"
     },
     {
-        "duration": "41.0",
+        "duration": "0.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/gmail.html"
     },
     {
-        "duration": "37.0",
+        "duration": "16.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/google.html"
     },
     {
-        "duration": "28.0",
+        "duration": "11.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/googlecalendar.html"
     },
     {
-        "duration": "14.0",
+        "duration": "12.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/googledocs.html"
     },
     {
-        "duration": "32.0",
+        "duration": "17.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/googleimagesearch.html"
     },
     {
-        "duration": "26.0",
+        "duration": "16.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/googleplus.html"
     },
     {
-        "duration": "26.0",
+        "duration": "13.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/linkedin.html"
     },
     {
-        "duration": "9.0",
+        "duration": "8.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/pinterest.html"
     },
     {
-        "duration": "21.0",
+        "duration": "15.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/techcrunch.html"
     },
     {
-        "duration": "42.0",
+        "duration": "21.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/twitter.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/weather.html"
     },
     {
@@ -1820,11 +1944,11 @@
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/wikipedia.html"
     },
     {
-        "duration": "13.0",
+        "duration": "12.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/wordpress.html"
     },
     {
-        "duration": "25.0",
+        "duration": "12.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahooanswers.html"
     },
     {
@@ -1836,31 +1960,31 @@
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahoonews.html"
     },
     {
-        "duration": "26.0",
+        "duration": "17.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/yahoosports.html"
     },
     {
-        "duration": "44.0",
+        "duration": "20.0",
         "name": "rasterize_and_record_micro.top_25/file://static_top_25/youtube.html"
     },
     {
-        "duration": "31.0",
+        "duration": "30.0",
         "name": "rendering.mobile/accu_weather_2018"
     },
     {
-        "duration": "39.0",
+        "duration": "37.0",
         "name": "rendering.mobile/accu_weather_mobile_pinch_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "rendering.mobile/amazon_2018"
     },
     {
-        "duration": "26.0",
+        "duration": "22.0",
         "name": "rendering.mobile/amazon_mobile_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "28.0",
         "name": "rendering.mobile/analog_clock_svg"
     },
     {
@@ -1868,43 +1992,43 @@
         "name": "rendering.mobile/androidpolice_mobile_2018"
     },
     {
-        "duration": "31.0",
+        "duration": "29.0",
         "name": "rendering.mobile/animometer_webgl"
     },
     {
-        "duration": "30.0",
+        "duration": "28.0",
         "name": "rendering.mobile/animometer_webgl_attrib_arrays"
     },
     {
-        "duration": "33.0",
+        "duration": "53.0",
         "name": "rendering.mobile/animometer_webgl_multi_draw"
     },
     {
-        "duration": "32.0",
+        "duration": "30.0",
         "name": "rendering.mobile/aquarium"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "rendering.mobile/aquarium_20k"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "rendering.mobile/background_color_animation"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/background_color_animation_with_gradient"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "rendering.mobile/baidu_mobile_2018"
     },
     {
-        "duration": "26.0",
+        "duration": "25.0",
         "name": "rendering.mobile/balls_css_key_frame_animations"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "rendering.mobile/balls_css_key_frame_animations_composited_transform"
     },
     {
@@ -1916,27 +2040,27 @@
         "name": "rendering.mobile/balls_css_transition_40_properties"
     },
     {
-        "duration": "24.0",
+        "duration": "25.0",
         "name": "rendering.mobile/balls_css_transition_all_properties"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/balls_javascript_canvas"
     },
     {
-        "duration": "25.0",
+        "duration": "23.0",
         "name": "rendering.mobile/balls_javascript_css"
     },
     {
-        "duration": "33.0",
+        "duration": "73.0",
         "name": "rendering.mobile/balls_svg_animations"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "rendering.mobile/basic_stream"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/bing_mobile_2018"
     },
     {
@@ -1944,111 +2068,87 @@
         "name": "rendering.mobile/blob"
     },
     {
-        "duration": "18.0",
+        "duration": "29.0",
         "name": "rendering.mobile/blogspot_2018"
     },
     {
-        "duration": "17.0",
+        "duration": "15.0",
         "name": "rendering.mobile/blogspot_mobile_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "rendering.mobile/blur_rotating_background"
     },
     {
-        "duration": "21.0",
+        "duration": "19.0",
         "name": "rendering.mobile/boingboing_mobile_2018"
     },
     {
-        "duration": "20.0",
+        "duration": "30.0",
         "name": "rendering.mobile/booking.com_2018"
     },
     {
-        "duration": "16.0",
+        "duration": "26.0",
         "name": "rendering.mobile/booking.com_mobile_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "rendering.mobile/bouncing_balls_15"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "rendering.mobile/bouncing_balls_shadow"
     },
     {
-        "duration": "24.0",
+        "duration": "27.0",
         "name": "rendering.mobile/bouncing_clipped_rectangles"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/bouncing_gradient_circles"
     },
     {
-        "duration": "23.0",
+        "duration": "20.0",
         "name": "rendering.mobile/bouncing_png_images"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.mobile/bouncing_svg_images"
     },
     {
-        "duration": "29.0",
+        "duration": "31.0",
         "name": "rendering.mobile/camera_to_webgl"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/canvas2d_to_texture.html"
     },
     {
-        "duration": "40.0",
-        "name": "rendering.mobile/canvas_05000_pixels_per_second"
-    },
-    {
-        "duration": "6.0",
+        "duration": "3.0",
         "name": "rendering.mobile/canvas_10000_pixels_per_second"
     },
     {
-        "duration": "6.0",
-        "name": "rendering.mobile/canvas_20000_pixels_per_second"
-    },
-    {
-        "duration": "6.0",
-        "name": "rendering.mobile/canvas_40000_pixels_per_second"
-    },
-    {
-        "duration": "6.0",
-        "name": "rendering.mobile/canvas_60000_pixels_per_second"
-    },
-    {
-        "duration": "6.0",
-        "name": "rendering.mobile/canvas_75000_pixels_per_second"
-    },
-    {
-        "duration": "6.0",
-        "name": "rendering.mobile/canvas_90000_pixels_per_second"
-    },
-    {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/canvas_animation_no_clear"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/canvas_arcs"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/canvas_font_cycler"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/canvas_lines"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/canvas_to_blob"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/canvas_to_canvas_draw"
     },
     {
@@ -2056,323 +2156,315 @@
         "name": "rendering.mobile/capitolvolkswagen_mobile_2018"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "rendering.mobile/card_expansion"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "rendering.mobile/card_expansion_animated"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "rendering.mobile/card_expansion_images_text"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "rendering.mobile/card_flying"
     },
     {
-        "duration": "14.0",
-        "name": "rendering.mobile/cats_unscaled"
-    },
-    {
-        "duration": "13.0",
+        "duration": "11.0",
         "name": "rendering.mobile/cats_viewport_width"
     },
     {
-        "duration": "27.0",
+        "duration": "36.0",
         "name": "rendering.mobile/cc_poster_circle"
     },
     {
-        "duration": "20.0",
+        "duration": "18.0",
         "name": "rendering.mobile/cc_scroll_text_only"
     },
     {
-        "duration": "26.0",
+        "duration": "29.0",
         "name": "rendering.mobile/chip_tune"
     },
     {
-        "duration": "20.0",
+        "duration": "24.0",
         "name": "rendering.mobile/cnn_2018"
     },
     {
-        "duration": "34.0",
+        "duration": "32.0",
         "name": "rendering.mobile/cnn_article_mobile_2018"
     },
     {
-        "duration": "27.0",
+        "duration": "23.0",
         "name": "rendering.mobile/cnn_mobile_2018"
     },
     {
-        "duration": "6.0",
+        "duration": "4.0",
         "name": "rendering.mobile/cnn_mobile_pinch_2018"
     },
     {
-        "duration": "22.0",
+        "duration": "25.0",
         "name": "rendering.mobile/cnn_pathological_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/compositor_heavy_animation"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "rendering.mobile/coordinated_animation"
     },
     {
-        "duration": "5.0",
+        "duration": "4.0",
         "name": "rendering.mobile/core_scroll_header_panel"
     },
     {
-        "duration": "26.0",
+        "duration": "23.0",
         "name": "rendering.mobile/crafty_mind"
     },
     {
-        "duration": "27.0",
+        "duration": "29.0",
         "name": "rendering.mobile/css_animations_many_keyframes"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_animations_simultaneous_inline_style"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_animations_simultaneous_new_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_animations_simultaneous_style_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_animations_simultaneous_updating_class"
     },
     {
-        "duration": "23.0",
+        "duration": "20.0",
         "name": "rendering.mobile/css_animations_staggered_infinite_iterations"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_staggered_inline_style"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_staggered_new_element"
     },
     {
-        "duration": "25.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_staggered_style_element"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_staggered_updating_class"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_triggered_inline_style"
     },
     {
-        "duration": "25.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_triggered_new_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_animations_triggered_style_element"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/css_animations_triggered_updating_class"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.mobile/css_opacity_plus_n_layers_0"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.mobile/css_opacity_plus_n_layers_75"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.mobile/css_opacity_plus_n_layers_99"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_inline_style"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_new_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_staggered_inline_style"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_staggered_new_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_staggered_style_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_staggered_updating_class"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_style_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_triggered_inline_style"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_triggered_new_element"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_triggered_style_element"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_triggered_updating_class"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_transitions_updating_class"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_color"
     },
     {
-        "duration": "23.0",
+        "duration": "20.0",
         "name": "rendering.mobile/css_value_type_filter"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_length"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_length_complex"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_length_simple"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_path"
     },
     {
-        "duration": "28.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_shadow"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/css_value_type_transform_complex"
     },
     {
-        "duration": "23.0",
+        "duration": "20.0",
         "name": "rendering.mobile/css_value_type_transform_simple"
     },
     {
-        "duration": "31.0",
+        "duration": "26.0",
         "name": "rendering.mobile/deviantart_mobile_2018"
     },
     {
-        "duration": "20.0",
+        "duration": "17.0",
         "name": "rendering.mobile/digg_mobile_2018"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/docs_paper.html"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/docs_resume.html"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/docs_table.html"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/draw_image"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/draw_image_not_pixel_aligned"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/dynamic_canvas_to_hw_accelerated_canvas.html"
     },
     {
-        "duration": "28.0",
+        "duration": "25.0",
         "name": "rendering.mobile/dynamic_cube_map"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "rendering.mobile/dynamic_webgl_to_hw_accelerated_canvas.html"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.mobile/earth"
     },
     {
-        "duration": "20.0",
+        "duration": "18.0",
         "name": "rendering.mobile/ebay_2018"
     },
     {
-        "duration": "16.0",
+        "duration": "14.0",
         "name": "rendering.mobile/ebay_mobile_2018"
     },
     {
-        "duration": "22.0",
+        "duration": "19.0",
         "name": "rendering.mobile/ebay_mobile_pinch_2018"
     },
     {
-        "duration": "28.0",
-        "name": "rendering.mobile/ebay_scroll_2018"
-    },
-    {
-        "duration": "5.0",
+        "duration": "4.0",
         "name": "rendering.mobile/effect_games"
     },
     {
-        "duration": "32.0",
+        "duration": "28.0",
         "name": "rendering.mobile/espn_2018"
     },
     {
-        "duration": "22.0",
+        "duration": "19.0",
         "name": "rendering.mobile/espn_mobile_2018"
     },
     {
-        "duration": "22.0",
+        "duration": "18.0",
         "name": "rendering.mobile/espn_pathological_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "21.0",
         "name": "rendering.mobile/extra_large_texture_uploads"
     },
     {
-        "duration": "25.0",
+        "duration": "20.0",
         "name": "rendering.mobile/facebook_2018"
     },
     {
-        "duration": "26.0",
+        "duration": "22.0",
         "name": "rendering.mobile/facebook_mobile_2018"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/falling_particle_simulation_cpu.html"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/falling_particle_simulation_gpu.html"
     },
     {
@@ -2380,59 +2472,55 @@
         "name": "rendering.mobile/famo_us_twitter_demo"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/fill_clear_rect.html"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/fill_shapes"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "rendering.mobile/filter_terrain_svg"
     },
     {
-        "duration": "28.0",
-        "name": "rendering.mobile/flickr_scroll_2018"
-    },
-    {
         "duration": "17.0",
         "name": "rendering.mobile/font_wipe"
     },
     {
-        "duration": "17.0",
+        "duration": "15.0",
         "name": "rendering.mobile/forecast.io_mobile_2018"
     },
     {
-        "duration": "5.0",
+        "duration": "4.0",
         "name": "rendering.mobile/geo_apis"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/get_image_data_cpu.html"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/get_image_data_gpu.html"
     },
     {
-        "duration": "30.0",
+        "duration": "26.0",
         "name": "rendering.mobile/gmail_2018"
     },
     {
-        "duration": "17.0",
+        "duration": "14.0",
         "name": "rendering.mobile/google_calendar_2018"
     },
     {
-        "duration": "26.0",
+        "duration": "23.0",
         "name": "rendering.mobile/google_docs_2018"
     },
     {
-        "duration": "20.0",
+        "duration": "17.0",
         "name": "rendering.mobile/google_image_search_2018"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/google_image_search_mobile_2018"
     },
     {
@@ -2440,131 +2528,123 @@
         "name": "rendering.mobile/google_news_ios"
     },
     {
-        "duration": "5.0",
+        "duration": "4.0",
         "name": "rendering.mobile/google_news_mobile_2018"
     },
     {
-        "duration": "22.0",
+        "duration": "17.0",
         "name": "rendering.mobile/google_plus_2018"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/google_plus_mobile_2018"
     },
     {
-        "duration": "20.0",
+        "duration": "17.0",
         "name": "rendering.mobile/google_search_mobile_pinch_2018"
     },
     {
-        "duration": "17.0",
+        "duration": "14.0",
         "name": "rendering.mobile/google_web_search_2018"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "rendering.mobile/google_web_search_mobile_2018"
     },
     {
-        "duration": "29.0",
+        "duration": "21.0",
         "name": "rendering.mobile/gpu_bound_shader.html"
     },
     {
-        "duration": "25.0",
+        "duration": "21.0",
         "name": "rendering.mobile/gsp.ro_mobile_2018"
     },
     {
-        "duration": "29.0",
+        "duration": "21.0",
         "name": "rendering.mobile/guardian_pathological_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/guimark_vector_chart"
     },
     {
-        "duration": "27.0",
+        "duration": "18.0",
         "name": "rendering.mobile/gws_boogie_expansion"
     },
     {
-        "duration": "24.0",
+        "duration": "18.0",
         "name": "rendering.mobile/gws_google_expansion"
     },
     {
-        "duration": "30.0",
+        "duration": "23.0",
         "name": "rendering.mobile/hakim"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "rendering.mobile/horizontal_vertical_expansion"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/hw_accelerated_canvas_to_sw_canvas.html"
     },
     {
-        "duration": "41.0",
+        "duration": "37.0",
         "name": "rendering.mobile/idle_power_animated_gif"
     },
     {
-        "duration": "35.0",
+        "duration": "32.0",
         "name": "rendering.mobile/idle_power_blank"
     },
     {
-        "duration": "44.0",
+        "duration": "37.0",
         "name": "rendering.mobile/idle_power_css_animation"
     },
     {
-        "duration": "37.0",
+        "duration": "36.0",
         "name": "rendering.mobile/idle_power_request_animation_frame"
     },
     {
-        "duration": "113.0",
-        "name": "rendering.mobile/idle_power_set_timeout_long"
-    },
-    {
-        "duration": "43.0",
-        "name": "rendering.mobile/idle_power_set_timetout"
-    },
-    {
-        "duration": "35.0",
+        "duration": "33.0",
         "name": "rendering.mobile/ie_chalkboard"
     },
     {
-        "duration": "26.0",
+        "duration": "25.0",
         "name": "rendering.mobile/ie_pirate_mark"
     },
     {
-        "duration": "28.0",
+        "duration": "23.0",
         "name": "rendering.mobile/infinite_scroll_element_n_layers_0"
     },
     {
-        "duration": "28.0",
+        "duration": "23.0",
         "name": "rendering.mobile/infinite_scroll_element_n_layers_75"
     },
     {
-        "duration": "29.0",
+        "duration": "27.0",
         "name": "rendering.mobile/infinite_scroll_element_n_layers_99"
     },
     {
-        "duration": "28.0",
+        "duration": "23.0",
         "name": "rendering.mobile/infinite_scroll_root_fixed_n_layers_0"
     },
     {
-        "duration": "28.0",
+        "duration": "24.0",
         "name": "rendering.mobile/infinite_scroll_root_fixed_n_layers_75"
     },
     {
-        "duration": "29.0",
+        "duration": "24.0",
         "name": "rendering.mobile/infinite_scroll_root_fixed_n_layers_99"
     },
     {
-        "duration": "28.0",
+        "duration": "23.0",
         "name": "rendering.mobile/infinite_scroll_root_n_layers_0"
     },
     {
-        "duration": "28.0",
+        "duration": "24.0",
         "name": "rendering.mobile/infinite_scroll_root_n_layers_75"
     },
     {
-        "duration": "29.0",
+        "duration": "24.0",
         "name": "rendering.mobile/infinite_scroll_root_n_layers_99"
     },
     {
@@ -2572,227 +2652,227 @@
         "name": "rendering.mobile/infinite_scrolling"
     },
     {
-        "duration": "32.0",
+        "duration": "26.0",
         "name": "rendering.mobile/jarro_doverson"
     },
     {
-        "duration": "20.0",
+        "duration": "16.0",
         "name": "rendering.mobile/jpeg_decoding_rgb_and_gpu_rasterization"
     },
     {
-        "duration": "20.0",
+        "duration": "16.0",
         "name": "rendering.mobile/jpeg_decoding_yuv_and_gpu_rasterization"
     },
     {
-        "duration": "35.0",
+        "duration": "28.0",
         "name": "rendering.mobile/js_full_screen_invalidation"
     },
     {
-        "duration": "30.0",
+        "duration": "25.0",
         "name": "rendering.mobile/js_opacity_plus_n_layers_0"
     },
     {
-        "duration": "30.0",
+        "duration": "25.0",
         "name": "rendering.mobile/js_opacity_plus_n_layers_75"
     },
     {
-        "duration": "30.0",
+        "duration": "25.0",
         "name": "rendering.mobile/js_opacity_plus_n_layers_99"
     },
     {
-        "duration": "30.0",
+        "duration": "25.0",
         "name": "rendering.mobile/js_paint_plus_n_layers_0"
     },
     {
-        "duration": "33.0",
+        "duration": "25.0",
         "name": "rendering.mobile/js_paint_plus_n_layers_75"
     },
     {
-        "duration": "30.0",
+        "duration": "25.0",
         "name": "rendering.mobile/js_paint_plus_n_layers_99"
     },
     {
-        "duration": "36.0",
+        "duration": "25.0",
         "name": "rendering.mobile/js_poster_circle"
     },
     {
-        "duration": "26.0",
+        "duration": "16.0",
         "name": "rendering.mobile/js_scroll_text_only"
     },
     {
-        "duration": "8.0",
+        "duration": "4.0",
         "name": "rendering.mobile/kevs_3d"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/keyframed_animations"
     },
     {
-        "duration": "26.0",
+        "duration": "22.0",
         "name": "rendering.mobile/large_texture_uploads"
     },
     {
-        "duration": "39.0",
+        "duration": "24.0",
         "name": "rendering.mobile/latimes_pathological_2018"
     },
     {
-        "duration": "31.0",
+        "duration": "21.0",
         "name": "rendering.mobile/linkedin_2018"
     },
     {
-        "duration": "58.0",
+        "duration": "46.0",
         "name": "rendering.mobile/linkedin_mobile_2018"
     },
     {
-        "duration": "54.0",
+        "duration": "42.0",
         "name": "rendering.mobile/linkedin_mobile_pinch_2018"
     },
     {
-        "duration": "58.0",
+        "duration": "38.0",
         "name": "rendering.mobile/linkedin_pathological_2018"
     },
     {
-        "duration": "15.0",
+        "duration": "14.0",
         "name": "rendering.mobile/list_animation_simple"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "rendering.mobile/list_recycle_transform"
     },
     {
-        "duration": "73.0",
+        "duration": "56.0",
         "name": "rendering.mobile/lost_crypt"
     },
     {
-        "duration": "26.0",
+        "duration": "22.0",
         "name": "rendering.mobile/main_0fps_impl_60fps"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_0fps_impl_60fps_no_update"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_0fps_impl_60fps_no_update_jank"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_0fps_with_jank_impl_0fps"
     },
     {
-        "duration": "29.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_15fps_impl_0fps"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_15fps_with_jank_impl_0fps"
     },
     {
-        "duration": "29.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_30fps_impl_0fps"
     },
     {
-        "duration": "52.0",
+        "duration": "22.0",
         "name": "rendering.mobile/main_30fps_impl_60fps"
     },
     {
-        "duration": "38.0",
+        "duration": "22.0",
         "name": "rendering.mobile/main_60fps_impl_0fps"
     },
     {
-        "duration": "27.0",
+        "duration": "22.0",
         "name": "rendering.mobile/main_60fps_impl_60fps"
     },
     {
-        "duration": "29.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_60fps_impl_60fps_no_update"
     },
     {
-        "duration": "26.0",
+        "duration": "21.0",
         "name": "rendering.mobile/main_60fps_impl_60fps_no_update_jank"
     },
     {
-        "duration": "46.0",
+        "duration": "38.0",
         "name": "rendering.mobile/main_60fps_with_extreme_jank_impl_0fps"
     },
     {
-        "duration": "29.0",
+        "duration": "22.0",
         "name": "rendering.mobile/main_60fps_with_jank_and_delay_impl_60fps"
     },
     {
-        "duration": "32.0",
+        "duration": "22.0",
         "name": "rendering.mobile/main_60fps_with_jank_impl_0fps"
     },
     {
-        "duration": "29.0",
+        "duration": "22.0",
         "name": "rendering.mobile/main_animations_half_presented"
     },
     {
-        "duration": "27.0",
+        "duration": "22.0",
         "name": "rendering.mobile/man_in_blue"
     },
     {
-        "duration": "34.0",
+        "duration": "28.0",
         "name": "rendering.mobile/many_images"
     },
     {
-        "duration": "34.0",
+        "duration": "25.0",
         "name": "rendering.mobile/many_planets_deep"
     },
     {
-        "duration": "8.0",
+        "duration": "4.0",
         "name": "rendering.mobile/maps_perf_test"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "rendering.mobile/mask_transition_animation"
     },
     {
-        "duration": "36.0",
+        "duration": "16.0",
         "name": "rendering.mobile/masonry"
     },
     {
-        "duration": "29.0",
+        "duration": "22.0",
         "name": "rendering.mobile/medium_texture_uploads"
     },
     {
-        "duration": "33.0",
+        "duration": "22.0",
         "name": "rendering.mobile/megi_dish"
     },
     {
-        "duration": "8.0",
+        "duration": "4.0",
         "name": "rendering.mobile/microgame_fps"
     },
     {
-        "duration": "29.0",
+        "duration": "23.0",
         "name": "rendering.mobile/microsoft_asteroid_belt"
     },
     {
-        "duration": "8.0",
+        "duration": "4.0",
         "name": "rendering.mobile/microsoft_fireflies"
     },
     {
-        "duration": "28.0",
+        "duration": "23.0",
         "name": "rendering.mobile/microsoft_fish_ie_tank"
     },
     {
-        "duration": "41.0",
+        "duration": "22.0",
         "name": "rendering.mobile/microsoft_performance"
     },
     {
-        "duration": "25.0",
+        "duration": "23.0",
         "name": "rendering.mobile/microsoft_snow"
     },
     {
-        "duration": "21.0",
+        "duration": "22.0",
         "name": "rendering.mobile/microsoft_speed_reading"
     },
     {
-        "duration": "22.0",
+        "duration": "23.0",
         "name": "rendering.mobile/microsoft_tweet_map"
     },
     {
-        "duration": "3.0",
+        "duration": "4.0",
         "name": "rendering.mobile/microsoft_video_city"
     },
     {
@@ -2812,7 +2892,7 @@
         "name": "rendering.mobile/mix_blend_mode_animation_hue"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/mix_blend_mode_animation_propagating_isolation"
     },
     {
@@ -2820,7 +2900,7 @@
         "name": "rendering.mobile/mix_blend_mode_animation_screen"
     },
     {
-        "duration": "37.0",
+        "duration": "29.0",
         "name": "rendering.mobile/mlb_mobile_2018"
     },
     {
@@ -2836,91 +2916,123 @@
         "name": "rendering.mobile/motion_mark_canvas_stroke_shapes"
     },
     {
-        "duration": "20.0",
+        "duration": "35.0",
         "name": "rendering.mobile/motionmark_anim_design_15"
     },
     {
-        "duration": "19.0",
+        "duration": "35.0",
         "name": "rendering.mobile/motionmark_anim_focus_25"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_anim_images_50"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_anim_leaves_250"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_anim_multiply_175"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_anim_suits_125"
     },
     {
-        "duration": "20.0",
+        "duration": "37.0",
         "name": "rendering.mobile/motionmark_html_composited_transforms_125"
     },
     {
-        "duration": "20.0",
+        "duration": "35.0",
         "name": "rendering.mobile/motionmark_html_css_bouncing_blend_circles_25"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_html_css_bouncing_circles_250"
     },
     {
-        "duration": "20.0",
+        "duration": "37.0",
         "name": "rendering.mobile/motionmark_html_css_bouncing_clipped_rects_100"
     },
     {
-        "duration": "20.0",
+        "duration": "35.0",
         "name": "rendering.mobile/motionmark_html_css_bouncing_filter_circles_15"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_html_css_bouncing_gradient_circles_250"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_html_css_bouncing_svg_images_50"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_html_css_bouncing_tagged_images_225"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_html_dom_particles_svg_masks_25"
     },
     {
-        "duration": "19.0",
+        "duration": "35.0",
         "name": "rendering.mobile/motionmark_html_focus_20_15"
     },
     {
-        "duration": "20.0",
+        "duration": "37.0",
         "name": "rendering.mobile/motionmark_html_leaves_20_50"
     },
     {
-        "duration": "20.0",
+        "duration": "43.0",
+        "name": "rendering.mobile/motionmark_ramp_canvas_arcs"
+    },
+    {
+        "duration": "42.0",
+        "name": "rendering.mobile/motionmark_ramp_canvas_lines"
+    },
+    {
+        "duration": "47.0",
+        "name": "rendering.mobile/motionmark_ramp_design"
+    },
+    {
+        "duration": "42.0",
+        "name": "rendering.mobile/motionmark_ramp_images"
+    },
+    {
+        "duration": "45.0",
+        "name": "rendering.mobile/motionmark_ramp_leaves"
+    },
+    {
+        "duration": "41.0",
+        "name": "rendering.mobile/motionmark_ramp_multiply"
+    },
+    {
+        "duration": "45.0",
+        "name": "rendering.mobile/motionmark_ramp_paths"
+    },
+    {
+        "duration": "46.0",
+        "name": "rendering.mobile/motionmark_ramp_suits"
+    },
+    {
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_svg_bouncing_circles_250"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_svg_bouncing_clipped_rects_100"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_svg_bouncing_gradient_circles_200"
     },
     {
-        "duration": "20.0",
+        "duration": "36.0",
         "name": "rendering.mobile/motionmark_svg_bouncing_png_images_200"
     },
     {
-        "duration": "20.0",
+        "duration": "39.0",
         "name": "rendering.mobile/motionmark_svg_bouncing_svg_images_50"
     },
     {
@@ -2928,47 +3040,51 @@
         "name": "rendering.mobile/new_tilings"
     },
     {
-        "duration": "21.0",
+        "duration": "25.0",
         "name": "rendering.mobile/no_op_raf"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "rendering.mobile/no_op_scroll"
     },
     {
-        "duration": "18.0",
+        "duration": "16.0",
         "name": "rendering.mobile/no_op_settimeout"
     },
     {
-        "duration": "18.0",
+        "duration": "17.0",
         "name": "rendering.mobile/no_op_touch_handler"
     },
     {
+        "duration": "34.0",
+        "name": "rendering.mobile/non_opaque_background_compositor_thread_scrolling_00050_pixels_per_second"
+    },
+    {
+        "duration": "35.0",
+        "name": "rendering.mobile/non_opaque_background_main_thread_scrolling_00050_pixels_per_second"
+    },
+    {
         "duration": "25.0",
         "name": "rendering.mobile/nvidia_vertex_buffer_object"
     },
     {
-        "duration": "28.0",
-        "name": "rendering.mobile/nyc_gov_scroll_2018"
-    },
-    {
-        "duration": "32.0",
+        "duration": "30.0",
         "name": "rendering.mobile/nytimes_mobile_2018"
     },
     {
-        "duration": "28.0",
-        "name": "rendering.mobile/nytimes_scroll_2018"
-    },
-    {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "rendering.mobile/off_screen_main_60fps"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "rendering.mobile/off_screen_main_60fps_jank"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
+        "name": "rendering.mobile/offscreen_animation_no_damage"
+    },
+    {
+        "duration": "23.0",
         "name": "rendering.mobile/overlay_background_color_css_transitions_page"
     },
     {
@@ -3008,7 +3124,7 @@
         "name": "rendering.mobile/paper_toggle_button"
     },
     {
-        "duration": "17.0",
+        "duration": "16.0",
         "name": "rendering.mobile/parallax_effect"
     },
     {
@@ -3020,15 +3136,15 @@
         "name": "rendering.mobile/pbs_pathological_2018"
     },
     {
-        "duration": "14.0",
+        "duration": "13.0",
         "name": "rendering.mobile/physical_simulation"
     },
     {
-        "duration": "25.0",
+        "duration": "24.0",
         "name": "rendering.mobile/pinterest_2018"
     },
     {
-        "duration": "12.0",
+        "duration": "11.0",
         "name": "rendering.mobile/pinterest_mobile_2018"
     },
     {
@@ -3040,11 +3156,11 @@
         "name": "rendering.mobile/put_and_create_imagebitmap_from_imagedata"
     },
     {
-        "duration": "22.0",
+        "duration": "49.0",
         "name": "rendering.mobile/put_get_image_data"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "rendering.mobile/put_image_data.html"
     },
     {
@@ -3076,75 +3192,19 @@
         "name": "rendering.mobile/runway_2019"
     },
     {
-        "duration": "28.0",
+        "duration": "25.0",
         "name": "rendering.mobile/san_angeles"
     },
     {
-        "duration": "15.0",
-        "name": "rendering.mobile/second_batch_js_heavy"
-    },
-    {
-        "duration": "14.0",
-        "name": "rendering.mobile/second_batch_js_light"
-    },
-    {
-        "duration": "14.0",
-        "name": "rendering.mobile/second_batch_js_medium"
-    },
-    {
         "duration": "23.0",
         "name": "rendering.mobile/sfgate_mobile_2018"
     },
     {
-        "duration": "24.0",
-        "name": "rendering.mobile/sheets_render.html"
-    },
-    {
-        "duration": "26.0",
-        "name": "rendering.mobile/silk_finance"
-    },
-    {
-        "duration": "20.0",
-        "name": "rendering.mobile/simple_text_page"
-    },
-    {
-        "duration": "16.0",
-        "name": "rendering.mobile/simple_touch_drag"
-    },
-    {
-        "duration": "64.0",
-        "name": "rendering.mobile/skelebuddies_wasm_2020"
-    },
-    {
-        "duration": "29.0",
-        "name": "rendering.mobile/slashdot_mobile_2018"
-    },
-    {
         "duration": "16.0",
         "name": "rendering.mobile/slide_drawer"
     },
     {
-        "duration": "24.0",
-        "name": "rendering.mobile/small_texture_uploads"
-    },
-    {
-        "duration": "6.0",
-        "name": "rendering.mobile/smash_cat"
-    },
-    {
-        "duration": "26.0",
-        "name": "rendering.mobile/spielzeugz"
-    },
-    {
-        "duration": "24.0",
-        "name": "rendering.mobile/static_canvas_to_hw_accelerated_canvas.html"
-    },
-    {
-        "duration": "24.0",
-        "name": "rendering.mobile/static_webgl_to_hw_accelerated_canvas.html"
-    },
-    {
-        "duration": "15.0",
+        "duration": "14.0",
         "name": "rendering.mobile/sticky_using_webkit"
     },
     {
@@ -3152,11 +3212,7 @@
         "name": "rendering.mobile/stress_hidey_bars"
     },
     {
-        "duration": "24.0",
-        "name": "rendering.mobile/stroke_shapes"
-    },
-    {
-        "duration": "29.0",
+        "duration": "25.0",
         "name": "rendering.mobile/svg_icon_raster"
     },
     {
@@ -3164,291 +3220,119 @@
         "name": "rendering.mobile/swipe_action"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "rendering.mobile/swipe_to_dismiss"
     },
     {
-        "duration": "19.0",
-        "name": "rendering.mobile/sync_scroll_offset"
+        "duration": "3.0",
+        "name": "rendering.mobile/text_scrollbar_100_pixels_per_second"
     },
     {
-        "duration": "30.0",
-        "name": "rendering.mobile/techcrunch_2018"
+        "duration": "3.0",
+        "name": "rendering.mobile/text_scrollbar_1200_pixels_per_second"
     },
     {
-        "duration": "25.0",
-        "name": "rendering.mobile/techcrunch_mobile_2018"
+        "duration": "3.0",
+        "name": "rendering.mobile/text_scrollbar_200_pixels_per_second"
     },
     {
-        "duration": "36.0",
-        "name": "rendering.mobile/text_05000_pixels_per_second"
+        "duration": "3.0",
+        "name": "rendering.mobile/text_scrollbar_2300_pixels_per_second"
     },
     {
-        "duration": "36.0",
-        "name": "rendering.mobile/text_10000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_20000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_40000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_60000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_75000_pixels_per_second"
-    },
-    {
-        "duration": "30.0",
-        "name": "rendering.mobile/text_90000_pixels_per_second"
-    },
-    {
-        "duration": "34.0",
-        "name": "rendering.mobile/text_constant_full_page_raster_05000_pixels_per_second"
-    },
-    {
-        "duration": "34.0",
-        "name": "rendering.mobile/text_constant_full_page_raster_10000_pixels_per_second"
-    },
-    {
-        "duration": "33.0",
-        "name": "rendering.mobile/text_constant_full_page_raster_20000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_constant_full_page_raster_40000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_constant_full_page_raster_60000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_constant_full_page_raster_75000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_constant_full_page_raster_90000_pixels_per_second"
-    },
-    {
-        "duration": "36.0",
-        "name": "rendering.mobile/text_hover_05000_pixels_per_second"
-    },
-    {
-        "duration": "36.0",
-        "name": "rendering.mobile/text_hover_10000_pixels_per_second"
-    },
-    {
-        "duration": "33.0",
-        "name": "rendering.mobile/text_hover_20000_pixels_per_second"
-    },
-    {
-        "duration": "32.0",
-        "name": "rendering.mobile/text_hover_40000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_hover_60000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_hover_75000_pixels_per_second"
-    },
-    {
-        "duration": "31.0",
-        "name": "rendering.mobile/text_hover_90000_pixels_per_second"
-    },
-    {
-        "duration": "25.0",
-        "name": "rendering.mobile/theverge_article_mobile_2018"
-    },
-    {
-        "duration": "25.0",
-        "name": "rendering.mobile/theverge_mobile_2018"
-    },
-    {
-        "duration": "27.0",
-        "name": "rendering.mobile/throughput_scrolling_active_handler"
-    },
-    {
-        "duration": "27.0",
-        "name": "rendering.mobile/throughput_scrolling_composited"
-    },
-    {
-        "duration": "30.0",
-        "name": "rendering.mobile/throughput_scrolling_passive_handler"
-    },
-    {
-        "duration": "27.0",
-        "name": "rendering.mobile/throughput_scrolling_uncomposited"
-    },
-    {
-        "duration": "59.0",
-        "name": "rendering.mobile/tiny_racing_v3_wasm_2020"
-    },
-    {
-        "duration": "6.0",
-        "name": "rendering.mobile/toBlob_duration.html"
-    },
-    {
-        "duration": "24.0",
-        "name": "rendering.mobile/toBlob_duration_jpeg.html"
-    },
-    {
-        "duration": "26.0",
-        "name": "rendering.mobile/toBlob_small_canvas_in_worker.html"
+        "duration": "3.0",
+        "name": "rendering.mobile/text_scrollbar_700_pixels_per_second"
     },
     {
         "duration": "21.0",
         "name": "rendering.mobile/toggle_drawer"
     },
     {
-        "duration": "20.0",
-        "name": "rendering.mobile/touch_handler_scrolling"
-    },
-    {
-        "duration": "24.0",
-        "name": "rendering.mobile/transfer_from_imageBitmap.html"
-    },
-    {
-        "duration": "23.0",
-        "name": "rendering.mobile/transform_transitions"
-    },
-    {
-        "duration": "23.0",
-        "name": "rendering.mobile/transform_transitions_js_block"
-    },
-    {
-        "duration": "24.0",
-        "name": "rendering.mobile/twitch_2018"
-    },
-    {
-        "duration": "26.0",
-        "name": "rendering.mobile/twitch_mobile_pinch_2018"
-    },
-    {
-        "duration": "26.0",
-        "name": "rendering.mobile/twitter_2018"
-    },
-    {
-        "duration": "20.0",
-        "name": "rendering.mobile/twitter_mobile_2018"
-    },
-    {
-        "duration": "20.0",
-        "name": "rendering.mobile/update_history_state"
-    },
-    {
-        "duration": "19.0",
-        "name": "rendering.mobile/usatoday_mobile_2018"
-    },
-    {
-        "duration": "18.0",
+        "duration": "16.0",
         "name": "rendering.mobile/vertical_expansion"
     },
     {
-        "duration": "26.0",
-        "name": "rendering.mobile/video_to_hw_accelerated_canvas"
-    },
-    {
-        "duration": "22.0",
-        "name": "rendering.mobile/video_to_sub_texture"
-    },
-    {
-        "duration": "22.0",
-        "name": "rendering.mobile/video_to_sub_texture_flip_and_premultiply"
-    },
-    {
-        "duration": "22.0",
-        "name": "rendering.mobile/video_to_sub_texture_flip_y"
-    },
-    {
-        "duration": "22.0",
+        "duration": "24.0",
         "name": "rendering.mobile/video_to_sub_texture_premultiply"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "rendering.mobile/video_to_texture"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "rendering.mobile/web_animation_value_type_color"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "rendering.mobile/web_animation_value_type_length_3d"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "rendering.mobile/web_animation_value_type_length_complex"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/web_animation_value_type_length_simple"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "rendering.mobile/web_animation_value_type_path"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/web_animation_value_type_shadow"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
         "name": "rendering.mobile/web_animation_value_type_transform_complex"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
         "name": "rendering.mobile/web_animation_value_type_transform_simple"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "rendering.mobile/web_animations_many_keyframes"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
         "name": "rendering.mobile/web_animations_set_current_time"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "rendering.mobile/web_animations_simultaneous"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/web_animations_staggered_chaining"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
         "name": "rendering.mobile/web_animations_staggered_infinite_iterations"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/web_animations_staggered_triggering_page"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "rendering.mobile/webgl_to_texture"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "rendering.mobile/webp_decoding_rgb_and_gpu_rasterization"
     },
     {
-        "duration": "16.0",
+        "duration": "14.0",
         "name": "rendering.mobile/webp_decoding_yuv_and_gpu_rasterization"
     },
     {
-        "duration": "29.0",
+        "duration": "26.0",
         "name": "rendering.mobile/wikipedia_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "27.0",
         "name": "rendering.mobile/wikipedia_delayed_scroll_start_2018"
     },
     {
@@ -3456,75 +3340,75 @@
         "name": "rendering.mobile/wikipedia_mobile_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/wordpress_2018"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "rendering.mobile/wordpress_mobile_2018"
     },
     {
-        "duration": "23.0",
+        "duration": "28.0",
         "name": "rendering.mobile/worldjournal_mobile_2018"
     },
     {
-        "duration": "28.0",
+        "duration": "37.0",
         "name": "rendering.mobile/wow_wiki_pathological_2018"
     },
     {
-        "duration": "43.0",
+        "duration": "39.0",
         "name": "rendering.mobile/wowwiki_mobile_2018"
     },
     {
-        "duration": "33.0",
+        "duration": "3.0",
         "name": "rendering.mobile/wsj_mobile_2018"
     },
     {
-        "duration": "16.0",
+        "duration": "14.0",
         "name": "rendering.mobile/yahoo_answers_2018"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "rendering.mobile/yahoo_answers_mobile_2018"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "rendering.mobile/yahoo_news_2018"
     },
     {
-        "duration": "22.0",
+        "duration": "44.0",
         "name": "rendering.mobile/yahoo_news_mobile_2018"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "rendering.mobile/yahoo_sports_2018"
     },
     {
-        "duration": "18.0",
+        "duration": "34.0",
         "name": "rendering.mobile/yahoo_sports_pathological_2018"
     },
     {
-        "duration": "4.0",
+        "duration": "3.0",
         "name": "rendering.mobile/youtube_2018"
     },
     {
-        "duration": "15.0",
+        "duration": "19.0",
         "name": "rendering.mobile/youtube_mobile_2018"
     },
     {
-        "duration": "21.0",
+        "duration": "26.0",
         "name": "rendering.mobile/zdnet_pathological_2018"
     },
     {
-        "duration": "16.0",
+        "duration": "15.0",
         "name": "rendering.mobile/zoom_in_animation"
     },
     {
-        "duration": "66.0",
+        "duration": "62.0",
         "name": "speedometer-future/http://browserbench.org/Speedometer/"
     },
     {
-        "duration": "65.0",
+        "duration": "61.0",
         "name": "speedometer/http://browserbench.org/Speedometer/"
     },
     {
@@ -3532,39 +3416,39 @@
         "name": "speedometer2-future/Speedometer2"
     },
     {
-        "duration": "113.0",
+        "duration": "125.0",
         "name": "speedometer2-pcscan/Speedometer2"
     },
     {
-        "duration": "110.0",
+        "duration": "109.0",
         "name": "speedometer2/Speedometer2"
     },
     {
-        "duration": "28.0",
+        "duration": "4.0",
         "name": "system_health.common_mobile/background:media:imgur:2019"
     },
     {
-        "duration": "63.0",
+        "duration": "71.0",
         "name": "system_health.common_mobile/background:news:nytimes:2019"
     },
     {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "system_health.common_mobile/background:search:google:2019"
     },
     {
-        "duration": "23.0",
+        "duration": "27.0",
         "name": "system_health.common_mobile/background:social:facebook:2019"
     },
     {
-        "duration": "35.0",
+        "duration": "38.0",
         "name": "system_health.common_mobile/background:tools:gmail:2019"
     },
     {
-        "duration": "8.0",
+        "duration": "7.0",
         "name": "system_health.common_mobile/browse:chrome:newtab:2019"
     },
     {
-        "duration": "9.0",
+        "duration": "7.0",
         "name": "system_health.common_mobile/browse:chrome:omnibox:2019"
     },
     {
@@ -3572,7 +3456,7 @@
         "name": "system_health.common_mobile/browse:media:facebook_photos:2019"
     },
     {
-        "duration": "46.0",
+        "duration": "47.0",
         "name": "system_health.common_mobile/browse:media:flickr_infinite_scroll:2019"
     },
     {
@@ -3580,23 +3464,35 @@
         "name": "system_health.common_mobile/browse:media:googleplaystore:2019"
     },
     {
-        "duration": "75.0",
+        "duration": "3.0",
         "name": "system_health.common_mobile/browse:media:imgur:2019"
     },
     {
+        "duration": "52.0",
+        "name": "system_health.common_mobile/browse:media:tiktok_infinite_scroll:2021"
+    },
+    {
         "duration": "56.0",
         "name": "system_health.common_mobile/browse:media:youtube:2019"
     },
     {
         "duration": "4.0",
+        "name": "system_health.common_mobile/browse:news:businessinsider:2021"
+    },
+    {
+        "duration": "132.0",
+        "name": "system_health.common_mobile/browse:news:cnn:2021"
+    },
+    {
+        "duration": "4.0",
         "name": "system_health.common_mobile/browse:news:cricbuzz:2019"
     },
     {
-        "duration": "54.0",
+        "duration": "63.0",
         "name": "system_health.common_mobile/browse:news:globo:2019"
     },
     {
-        "duration": "87.0",
+        "duration": "90.0",
         "name": "system_health.common_mobile/browse:news:nytimes:2019"
     },
     {
@@ -3604,35 +3500,35 @@
         "name": "system_health.common_mobile/browse:news:qq:2019"
     },
     {
-        "duration": "58.0",
+        "duration": "62.0",
         "name": "system_health.common_mobile/browse:news:reddit:2019"
     },
     {
-        "duration": "74.0",
+        "duration": "91.0",
         "name": "system_health.common_mobile/browse:news:toi:2019"
     },
     {
-        "duration": "62.0",
+        "duration": "63.0",
         "name": "system_health.common_mobile/browse:news:washingtonpost:2019"
     },
     {
-        "duration": "24.0",
+        "duration": "25.0",
         "name": "system_health.common_mobile/browse:search:amp:2018"
     },
     {
-        "duration": "25.0",
+        "duration": "24.0",
         "name": "system_health.common_mobile/browse:search:amp:sxg:2019"
     },
     {
-        "duration": "57.0",
+        "duration": "70.0",
         "name": "system_health.common_mobile/browse:shopping:amazon:2019"
     },
     {
-        "duration": "57.0",
+        "duration": "60.0",
         "name": "system_health.common_mobile/browse:shopping:avito:2019"
     },
     {
-        "duration": "63.0",
+        "duration": "82.0",
         "name": "system_health.common_mobile/browse:shopping:flipkart:2019"
     },
     {
@@ -3644,7 +3540,7 @@
         "name": "system_health.common_mobile/browse:social:facebook:2019"
     },
     {
-        "duration": "75.0",
+        "duration": "94.0",
         "name": "system_health.common_mobile/browse:social:facebook_infinite_scroll:2018"
     },
     {
@@ -3652,63 +3548,67 @@
         "name": "system_health.common_mobile/browse:social:instagram:2019"
     },
     {
-        "duration": "66.0",
+        "duration": "85.0",
+        "name": "system_health.common_mobile/browse:social:pinterest_infinite_scroll:2021"
+    },
+    {
+        "duration": "67.0",
         "name": "system_health.common_mobile/browse:social:tumblr_infinite_scroll:2018"
     },
     {
-        "duration": "69.0",
+        "duration": "76.0",
         "name": "system_health.common_mobile/browse:social:twitter:2019"
     },
     {
-        "duration": "71.0",
+        "duration": "88.0",
         "name": "system_health.common_mobile/browse:tech:discourse_infinite_scroll:2018"
     },
     {
-        "duration": "42.0",
+        "duration": "43.0",
         "name": "system_health.common_mobile/browse:tools:maps:2019"
     },
     {
-        "duration": "22.0",
+        "duration": "24.0",
         "name": "system_health.common_mobile/load:chrome:blank"
     },
     {
-        "duration": "20.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:games:bubbles:2020"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:games:lazors"
     },
     {
-        "duration": "26.0",
+        "duration": "25.0",
         "name": "system_health.common_mobile/load:games:spychase:2018"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "system_health.common_mobile/load:media:dailymotion:2019"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:media:facebook_feed:mobile:2020"
     },
     {
-        "duration": "20.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:media:facebook_photos:2019"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:media:facebook_photos:mobile:2020"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "system_health.common_mobile/load:media:flickr:2018"
     },
     {
-        "duration": "21.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:media:google_images:2018"
     },
     {
-        "duration": "27.0",
+        "duration": "26.0",
         "name": "system_health.common_mobile/load:media:imgur:2018"
     },
     {
@@ -3720,47 +3620,47 @@
         "name": "system_health.common_mobile/load:media:youtube:2018"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:news:bbc:2019"
     },
     {
-        "duration": "27.0",
+        "duration": "26.0",
         "name": "system_health.common_mobile/load:news:cnn:2020"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.common_mobile/load:news:irctc:2019"
     },
     {
-        "duration": "31.0",
+        "duration": "29.0",
         "name": "system_health.common_mobile/load:news:nytimes:2019"
     },
     {
-        "duration": "22.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:news:qq:2019"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:news:reddit:2019"
     },
     {
-        "duration": "21.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:news:washingtonpost:2019"
     },
     {
-        "duration": "20.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:news:wikipedia:2018"
     },
     {
-        "duration": "22.0",
+        "duration": "21.0",
         "name": "system_health.common_mobile/load:search:amazon:2019"
     },
     {
-        "duration": "23.0",
+        "duration": "21.0",
         "name": "system_health.common_mobile/load:search:baidu:2018"
     },
     {
-        "duration": "21.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:search:ebay:2018"
     },
     {
@@ -3768,23 +3668,23 @@
         "name": "system_health.common_mobile/load:search:google:2018"
     },
     {
-        "duration": "20.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:search:taobao:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:search:yahoo:2018"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:search:yandex:2018"
     },
     {
-        "duration": "20.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:social:instagram:2019"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.common_mobile/load:social:pinterest:2019"
     },
     {
@@ -3792,27 +3692,27 @@
         "name": "system_health.common_mobile/load:social:twitter:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "system_health.common_mobile/load:tools:docs:2019"
     },
     {
-        "duration": "20.0",
+        "duration": "19.0",
         "name": "system_health.common_mobile/load:tools:drive:2019"
     },
     {
-        "duration": "36.0",
+        "duration": "34.0",
         "name": "system_health.common_mobile/load:tools:dropbox:2019"
     },
     {
-        "duration": "31.0",
+        "duration": "29.0",
         "name": "system_health.common_mobile/load:tools:gmail:2019"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:tools:stackoverflow:2018"
     },
     {
-        "duration": "21.0",
+        "duration": "20.0",
         "name": "system_health.common_mobile/load:tools:weather:2019"
     },
     {
@@ -3824,35 +3724,31 @@
         "name": "system_health.common_mobile/long_running:tools:gmail-foreground"
     },
     {
-        "duration": "44.0",
+        "duration": "30.0",
         "name": "system_health.memory_mobile/background:media:imgur:2019"
     },
     {
-        "duration": "67.0",
+        "duration": "63.0",
         "name": "system_health.memory_mobile/background:news:nytimes:2019"
     },
     {
-        "duration": "35.0",
+        "duration": "27.0",
         "name": "system_health.memory_mobile/background:search:google:2019"
     },
     {
-        "duration": "27.0",
+        "duration": "24.0",
         "name": "system_health.memory_mobile/background:social:facebook:2019"
     },
     {
-        "duration": "36.0",
+        "duration": "35.0",
         "name": "system_health.memory_mobile/background:tools:gmail:2019"
     },
     {
-        "duration": "13.0",
-        "name": "system_health.memory_mobile/browse:chrome:newtab:2019"
-    },
-    {
-        "duration": "12.0",
+        "duration": "9.0",
         "name": "system_health.memory_mobile/browse:chrome:omnibox:2019"
     },
     {
-        "duration": "80.0",
+        "duration": "75.0",
         "name": "system_health.memory_mobile/browse:media:facebook_photos:2019"
     },
     {
@@ -3860,75 +3756,79 @@
         "name": "system_health.memory_mobile/browse:media:flickr_infinite_scroll:2019"
     },
     {
-        "duration": "45.0",
+        "duration": "44.0",
         "name": "system_health.memory_mobile/browse:media:googleplaystore:2019"
     },
     {
-        "duration": "74.0",
+        "duration": "73.0",
         "name": "system_health.memory_mobile/browse:media:imgur:2019"
     },
     {
-        "duration": "78.0",
+        "duration": "51.0",
+        "name": "system_health.memory_mobile/browse:media:tiktok_infinite_scroll:2021"
+    },
+    {
+        "duration": "57.0",
         "name": "system_health.memory_mobile/browse:media:youtube:2019"
     },
     {
-        "duration": "5.0",
+        "duration": "140.0",
+        "name": "system_health.memory_mobile/browse:news:businessinsider:2021"
+    },
+    {
+        "duration": "68.0",
+        "name": "system_health.memory_mobile/browse:news:cnn:2021"
+    },
+    {
+        "duration": "2.0",
         "name": "system_health.memory_mobile/browse:news:cricbuzz:2019"
     },
     {
-        "duration": "56.0",
+        "duration": "52.0",
         "name": "system_health.memory_mobile/browse:news:globo:2019"
     },
     {
-        "duration": "87.0",
-        "name": "system_health.memory_mobile/browse:news:nytimes:2019"
-    },
-    {
-        "duration": "42.0",
-        "name": "system_health.memory_mobile/browse:news:qq:2019"
-    },
-    {
-        "duration": "64.0",
+        "duration": "65.0",
         "name": "system_health.memory_mobile/browse:news:reddit:2019"
     },
     {
-        "duration": "75.0",
+        "duration": "64.0",
         "name": "system_health.memory_mobile/browse:news:toi:2019"
     },
     {
-        "duration": "63.0",
+        "duration": "65.0",
         "name": "system_health.memory_mobile/browse:news:washingtonpost:2019"
     },
     {
-        "duration": "28.0",
+        "duration": "26.0",
         "name": "system_health.memory_mobile/browse:search:amp:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "26.0",
         "name": "system_health.memory_mobile/browse:search:amp:sxg:2019"
     },
     {
-        "duration": "61.0",
+        "duration": "57.0",
         "name": "system_health.memory_mobile/browse:shopping:amazon:2019"
     },
     {
-        "duration": "61.0",
+        "duration": "54.0",
         "name": "system_health.memory_mobile/browse:shopping:avito:2019"
     },
     {
-        "duration": "66.0",
+        "duration": "62.0",
         "name": "system_health.memory_mobile/browse:shopping:flipkart:2019"
     },
     {
-        "duration": "5.0",
+        "duration": "2.0",
         "name": "system_health.memory_mobile/browse:shopping:lazada:2019"
     },
     {
-        "duration": "5.0",
+        "duration": "2.0",
         "name": "system_health.memory_mobile/browse:social:facebook:2019"
     },
     {
-        "duration": "5.0",
+        "duration": "2.0",
         "name": "system_health.memory_mobile/browse:social:facebook_infinite_scroll:2018"
     },
     {
@@ -3940,71 +3840,55 @@
         "name": "system_health.memory_mobile/browse:social:tumblr_infinite_scroll:2018"
     },
     {
-        "duration": "73.0",
+        "duration": "82.0",
         "name": "system_health.memory_mobile/browse:social:twitter:2019"
     },
     {
-        "duration": "5.0",
+        "duration": "2.0",
         "name": "system_health.memory_mobile/browse:tech:discourse_infinite_scroll:2018"
     },
     {
-        "duration": "45.0",
+        "duration": "44.0",
         "name": "system_health.memory_mobile/browse:tools:maps:2019"
     },
     {
-        "duration": "26.0",
+        "duration": "22.0",
         "name": "system_health.memory_mobile/load:chrome:blank"
     },
     {
-        "duration": "23.0",
+        "duration": "22.0",
         "name": "system_health.memory_mobile/load:games:bubbles:2020"
     },
     {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:games:lazors"
-    },
-    {
-        "duration": "33.0",
-        "name": "system_health.memory_mobile/load:games:spychase:2018"
-    },
-    {
-        "duration": "26.0",
+        "duration": "25.0",
         "name": "system_health.memory_mobile/load:media:dailymotion:2019"
     },
     {
-        "duration": "25.0",
+        "duration": "23.0",
         "name": "system_health.memory_mobile/load:media:facebook_feed:mobile:2020"
     },
     {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "system_health.memory_mobile/load:media:facebook_photos:2019"
     },
     {
-        "duration": "25.0",
+        "duration": "24.0",
         "name": "system_health.memory_mobile/load:media:facebook_photos:mobile:2020"
     },
     {
-        "duration": "30.0",
+        "duration": "27.0",
         "name": "system_health.memory_mobile/load:media:flickr:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "23.0",
         "name": "system_health.memory_mobile/load:media:google_images:2018"
     },
     {
-        "duration": "35.0",
+        "duration": "27.0",
         "name": "system_health.memory_mobile/load:media:imgur:2018"
     },
     {
-        "duration": "6.0",
-        "name": "system_health.memory_mobile/load:media:soundcloud:2018"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:media:youtube:2018"
-    },
-    {
-        "duration": "25.0",
+        "duration": "24.0",
         "name": "system_health.memory_mobile/load:news:bbc:2019"
     },
     {
@@ -4012,259 +3896,219 @@
         "name": "system_health.memory_mobile/load:news:cnn:2020"
     },
     {
-        "duration": "30.0",
-        "name": "system_health.memory_mobile/load:news:irctc:2019"
-    },
-    {
-        "duration": "36.0",
-        "name": "system_health.memory_mobile/load:news:nytimes:2019"
-    },
-    {
-        "duration": "29.0",
-        "name": "system_health.memory_mobile/load:news:qq:2019"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:news:reddit:2019"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:news:washingtonpost:2019"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:news:wikipedia:2018"
-    },
-    {
-        "duration": "26.0",
+        "duration": "24.0",
         "name": "system_health.memory_mobile/load:search:amazon:2019"
     },
     {
-        "duration": "28.0",
+        "duration": "26.0",
         "name": "system_health.memory_mobile/load:search:baidu:2018"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "system_health.memory_mobile/load:search:ebay:2018"
     },
     {
-        "duration": "28.0",
+        "duration": "23.0",
         "name": "system_health.memory_mobile/load:search:google:2018"
     },
     {
-        "duration": "27.0",
-        "name": "system_health.memory_mobile/load:search:taobao:2019"
-    },
-    {
-        "duration": "26.0",
-        "name": "system_health.memory_mobile/load:search:yahoo:2018"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:search:yandex:2018"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:social:instagram:2019"
-    },
-    {
-        "duration": "30.0",
-        "name": "system_health.memory_mobile/load:social:pinterest:2019"
-    },
-    {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:social:twitter:2019"
-    },
-    {
-        "duration": "24.0",
+        "duration": "22.0",
         "name": "system_health.memory_mobile/load:tools:docs:2019"
     },
     {
-        "duration": "24.0",
+        "duration": "23.0",
         "name": "system_health.memory_mobile/load:tools:drive:2019"
     },
     {
-        "duration": "40.0",
+        "duration": "38.0",
         "name": "system_health.memory_mobile/load:tools:dropbox:2019"
     },
     {
-        "duration": "38.0",
+        "duration": "33.0",
         "name": "system_health.memory_mobile/load:tools:gmail:2019"
     },
     {
-        "duration": "29.0",
-        "name": "system_health.memory_mobile/load:tools:stackoverflow:2018"
+        "duration": "0.0",
+        "name": "system_health.pcscan/browse:news:cnn:2021"
     },
     {
-        "duration": "28.0",
-        "name": "system_health.memory_mobile/load:tools:weather:2019"
-    },
-    {
-        "duration": "6.0",
-        "name": "system_health.memory_mobile/long_running:tools:gmail-background"
-    },
-    {
-        "duration": "6.0",
-        "name": "system_health.memory_mobile/long_running:tools:gmail-foreground"
-    },
-    {
-        "duration": "31.0",
+        "duration": "21.0",
         "name": "system_health.webview_startup/load:chrome:blank"
     },
     {
-        "duration": "16.0",
+        "duration": "12.0",
         "name": "tracing.tracing_with_background_memory_infra/Facebook"
     },
     {
-        "duration": "14.0",
+        "duration": "11.0",
         "name": "tracing.tracing_with_background_memory_infra/Wikipedia"
     },
     {
-        "duration": "12.0",
+        "duration": "8.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.amazon.com"
     },
     {
-        "duration": "12.0",
+        "duration": "8.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.ask.com/"
     },
     {
-        "duration": "12.0",
+        "duration": "9.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.bing.com/"
     },
     {
-        "duration": "13.0",
+        "duration": "9.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.yahoo.com/"
     },
     {
-        "duration": "14.0",
+        "duration": "10.0",
         "name": "tracing.tracing_with_background_memory_infra/http://www.youtube.com"
     },
     {
-        "duration": "16.0",
+        "duration": "10.0",
         "name": "tracing.tracing_with_background_memory_infra/https://www.google.com/#hl=en&q=barack+obama"
     },
     {
-        "duration": "15.0",
+        "duration": "11.0",
         "name": "tracing.tracing_with_background_memory_infra/https://www.google.com/calendar/"
     },
     {
-        "duration": "47.0",
+        "duration": "12.0",
         "name": "v8.browsing_mobile/browse:chrome:newtab:2019"
     },
     {
-        "duration": "31.0",
+        "duration": "18.0",
         "name": "v8.browsing_mobile/browse:chrome:omnibox:2019"
     },
     {
-        "duration": "115.0",
+        "duration": "110.0",
         "name": "v8.browsing_mobile/browse:media:facebook_photos:2019"
     },
     {
-        "duration": "69.0",
+        "duration": "58.0",
         "name": "v8.browsing_mobile/browse:media:flickr_infinite_scroll:2019"
     },
     {
-        "duration": "67.0",
+        "duration": "49.0",
         "name": "v8.browsing_mobile/browse:media:googleplaystore:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "8.0",
         "name": "v8.browsing_mobile/browse:media:imgur:2019"
     },
     {
-        "duration": "80.0",
+        "duration": "77.0",
+        "name": "v8.browsing_mobile/browse:media:tiktok_infinite_scroll:2021"
+    },
+    {
+        "duration": "66.0",
         "name": "v8.browsing_mobile/browse:media:youtube:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "8.0",
+        "name": "v8.browsing_mobile/browse:news:businessinsider:2021"
+    },
+    {
+        "duration": "85.0",
+        "name": "v8.browsing_mobile/browse:news:cnn:2021"
+    },
+    {
+        "duration": "8.0",
         "name": "v8.browsing_mobile/browse:news:cricbuzz:2019"
     },
     {
-        "duration": "75.0",
+        "duration": "63.0",
         "name": "v8.browsing_mobile/browse:news:globo:2019"
     },
     {
-        "duration": "148.0",
+        "duration": "161.0",
         "name": "v8.browsing_mobile/browse:news:nytimes:2019"
     },
     {
-        "duration": "57.0",
+        "duration": "44.0",
         "name": "v8.browsing_mobile/browse:news:qq:2019"
     },
     {
-        "duration": "83.0",
+        "duration": "72.0",
         "name": "v8.browsing_mobile/browse:news:reddit:2019"
     },
     {
-        "duration": "105.0",
+        "duration": "82.0",
         "name": "v8.browsing_mobile/browse:news:toi:2019"
     },
     {
-        "duration": "95.0",
+        "duration": "75.0",
         "name": "v8.browsing_mobile/browse:news:washingtonpost:2019"
     },
     {
-        "duration": "43.0",
+        "duration": "30.0",
         "name": "v8.browsing_mobile/browse:search:amp:2018"
     },
     {
-        "duration": "42.0",
+        "duration": "29.0",
         "name": "v8.browsing_mobile/browse:search:amp:sxg:2019"
     },
     {
-        "duration": "80.0",
+        "duration": "67.0",
         "name": "v8.browsing_mobile/browse:shopping:amazon:2019"
     },
     {
-        "duration": "80.0",
+        "duration": "65.0",
         "name": "v8.browsing_mobile/browse:shopping:avito:2019"
     },
     {
-        "duration": "87.0",
+        "duration": "74.0",
         "name": "v8.browsing_mobile/browse:shopping:flipkart:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "8.0",
         "name": "v8.browsing_mobile/browse:shopping:lazada:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "8.0",
         "name": "v8.browsing_mobile/browse:social:facebook:2019"
     },
     {
-        "duration": "103.0",
+        "duration": "86.0",
         "name": "v8.browsing_mobile/browse:social:facebook_infinite_scroll:2018"
     },
     {
-        "duration": "19.0",
+        "duration": "8.0",
         "name": "v8.browsing_mobile/browse:social:instagram:2019"
     },
     {
-        "duration": "93.0",
+        "duration": "106.0",
+        "name": "v8.browsing_mobile/browse:social:pinterest_infinite_scroll:2021"
+    },
+    {
+        "duration": "80.0",
         "name": "v8.browsing_mobile/browse:social:tumblr_infinite_scroll:2018"
     },
     {
-        "duration": "102.0",
+        "duration": "89.0",
         "name": "v8.browsing_mobile/browse:social:twitter:2019"
     },
     {
-        "duration": "94.0",
+        "duration": "8.0",
         "name": "v8.browsing_mobile/browse:tech:discourse_infinite_scroll:2018"
     },
     {
-        "duration": "83.0",
+        "duration": "50.0",
         "name": "v8.browsing_mobile/browse:tools:maps:2019"
     },
     {
-        "duration": "19.0",
+        "duration": "349.0",
+        "name": "wasmpspdfkit/https://pspdfkit.com/webassembly-benchmark/"
+    },
+    {
+        "duration": "18.0",
         "name": "webrtc/10s_datachannel_transfer"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "webrtc/canvas_capture_peer_connection"
     },
     {
-        "duration": "29.0",
+        "duration": "28.0",
         "name": "webrtc/codec_constraints_h264"
     },
     {
@@ -4272,39 +4116,51 @@
         "name": "webrtc/codec_constraints_vp8"
     },
     {
-        "duration": "29.0",
+        "duration": "28.0",
         "name": "webrtc/codec_constraints_vp9"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "webrtc/hd_local_stream_10s"
     },
     {
-        "duration": "20.0",
+        "duration": "18.0",
+        "name": "webrtc/insertable_streams_audio_processing"
+    },
+    {
+        "duration": "18.0",
         "name": "webrtc/insertable_streams_video_processing_camera_canvas2d_video"
     },
     {
-        "duration": "20.0",
+        "duration": "18.0",
+        "name": "webrtc/insertable_streams_video_processing_camera_noop_video"
+    },
+    {
+        "duration": "18.0",
         "name": "webrtc/insertable_streams_video_processing_camera_webgl_pc"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "webrtc/insertable_streams_video_processing_camera_webgl_video"
     },
     {
-        "duration": "19.0",
+        "duration": "18.0",
         "name": "webrtc/insertable_streams_video_processing_pc_webgl_video"
     },
     {
-        "duration": "20.0",
+        "duration": "19.0",
         "name": "webrtc/insertable_streams_video_processing_video_webgl_video"
     },
     {
-        "duration": "30.0",
+        "duration": "28.0",
         "name": "webrtc/multiple_peerconnections"
     },
     {
-        "duration": "32.0",
+        "duration": "12.0",
+        "name": "webrtc/negotiate-timing"
+    },
+    {
+        "duration": "30.0",
         "name": "webrtc/pause_play_peerconnections"
     }
 ]
\ No newline at end of file
diff --git a/tools/traffic_annotation/summary/BUILD.gn b/tools/traffic_annotation/summary/BUILD.gn
index 25faae4..979c194 100644
--- a/tools/traffic_annotation/summary/BUILD.gn
+++ b/tools/traffic_annotation/summary/BUILD.gn
@@ -3,6 +3,11 @@
 # found in the LICENSE file.
 
 copy("annotations_xml") {
-  sources = [ "annotations.xml" ]
+  # This rule is referenced by src/BUILD.gn, and auditor.py runs on CQ if any of
+  # these files are modified:
+  sources = [
+    "annotations.xml",
+    "grouping.xml",
+  ]
   outputs = [ "$target_out_dir/{{source_file_part}}" ]
 }
diff --git a/tools/traffic_annotation/summary/annotations.xml b/tools/traffic_annotation/summary/annotations.xml
index 7e86dc9..c5d4dd53 100644
--- a/tools/traffic_annotation/summary/annotations.xml
+++ b/tools/traffic_annotation/summary/annotations.xml
@@ -301,7 +301,6 @@
  <item id="fast_pair_footprints_request" added_in_milestone="98" type="partial" second_id="oauth2_api_call_flow" content_hash_code="0181e407" os_list="chromeos" semantics_fields="1,2,3,4,5" policy_fields="-1,3,5" file_path="ash/quick_pair/repository/fast_pair/footprints_fetcher.cc" />
  <item id="kiosk_app_icon" added_in_milestone="98" content_hash_code="04f02fef" os_list="chromeos" file_path="chrome/browser/ash/app_mode/web_app/web_kiosk_app_data.cc" />
  <item id="arc_auth_code_fetcher" added_in_milestone="98" content_hash_code="057519ca" os_list="chromeos" file_path="chrome/browser/ash/arc/auth/arc_background_auth_code_fetcher.cc" />
- <item id="secondary_account_consent_logger" added_in_milestone="98" content_hash_code="015db79a" os_list="chromeos" file_path="chrome/browser/ash/child_accounts/secondary_account_consent_logger.cc" />
  <item id="customization_wallpaper_downloader" added_in_milestone="98" content_hash_code="03ee8364" os_list="chromeos" file_path="chrome/browser/ash/customization/customization_wallpaper_downloader.cc" />
  <item id="ime_url_downloader" added_in_milestone="98" content_hash_code="00f13105" os_list="chromeos" file_path="chrome/browser/ash/input_method/ime_service_connector.cc" />
  <item id="chromebook_mail_api" added_in_milestone="98" content_hash_code="01bfcf72" os_list="chromeos" file_path="chrome/browser/ash/login/marketing_backend_connector.cc" />
@@ -348,6 +347,8 @@
  <item id="minidump_uploader_android" added_in_milestone="98" content_hash_code="0327544e" os_list="android" file_path="components/minidump_uploader/android/java/src/org/chromium/components/minidump_uploader/util/HttpURLConnectionFactoryImpl.java" />
  <item id="chrome_variations_android" added_in_milestone="98" content_hash_code="04eeb61f" os_list="android" file_path="components/variations/android/java/src/org/chromium/components/variations/firstrun/VariationsSeedFetcher.java" />
  <item id="wallpaper_google_photos_count" added_in_milestone="99" content_hash_code="073c3f10" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
- <item id="backdrop_images_info_download" added_in_milestone="99" content_hash_code="030771dc" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
- <item id="backdrop_surprise_me_image_download" added_in_milestone="99" content_hash_code="06f0b87b" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
+ <item id="wallpaper_backdrop_collection_names" added_in_milestone="99" content_hash_code="06379fce" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
+ <item id="wallpaper_backdrop_images_info" added_in_milestone="99" content_hash_code="0010428c" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
+ <item id="wallpaper_backdrop_surprise_me_image" added_in_milestone="99" content_hash_code="0383b9db" os_list="chromeos" file_path="chrome/browser/ash/wallpaper_handlers/wallpaper_handlers.cc" />
+ <item id="nearby_connections_wifi_lan" added_in_milestone="99" content_hash_code="06b30010" os_list="chromeos" file_path="chrome/services/sharing/nearby/platform/wifi_lan_medium.cc" />
 </annotations>
diff --git a/tools/traffic_annotation/summary/grouping.xml b/tools/traffic_annotation/summary/grouping.xml
index 8caaba5e..9b126c2c 100644
--- a/tools/traffic_annotation/summary/grouping.xml
+++ b/tools/traffic_annotation/summary/grouping.xml
@@ -56,8 +56,6 @@
       <traffic_annotation unique_id="ambient_photo_cache"/>
       <traffic_annotation unique_id="ambient_photo_controller"/>
       <traffic_annotation unique_id="app_suggestion_get_favicon"/>
-      <traffic_annotation unique_id="backdrop_images_info_download"/>
-      <traffic_annotation unique_id="backdrop_surprise_me_image_download"/>
       <traffic_annotation unique_id="arc_auth_code_fetcher"/>
       <traffic_annotation unique_id="calendar_get_events"/>
       <traffic_annotation unique_id="chrome_plugin_vm_api"/>
@@ -72,16 +70,19 @@
       <traffic_annotation unique_id="kiosk_app_icon"/>
       <traffic_annotation unique_id="launcher_item_suggest"/>
       <traffic_annotation unique_id="management_ui_customer_logo"/>
+      <traffic_annotation unique_id="nearby_connections_wifi_lan"/>
       <traffic_annotation unique_id="network_traversal_ice_config_fetcher"/>
       <traffic_annotation unique_id="remote_apps_image_downloader"/>
       <traffic_annotation unique_id="remote_command_screenshot"/>
-      <traffic_annotation unique_id="secondary_account_consent_logger"/>
       <traffic_annotation unique_id="side_search_availability_test"/>
       <traffic_annotation unique_id="smb_netbios_name_query"/>
       <traffic_annotation unique_id="supervised_users_denylist"/>
       <traffic_annotation unique_id="tachyon_ice_config_fetcher"/>
       <traffic_annotation unique_id="terms_of_service_fetch"/>
       <traffic_annotation unique_id="url_icon_source_fetch"/>
+      <traffic_annotation unique_id="wallpaper_backdrop_collection_names"/>
+      <traffic_annotation unique_id="wallpaper_backdrop_images_info"/>
+      <traffic_annotation unique_id="wallpaper_backdrop_surprise_me_image"/>
       <traffic_annotation unique_id="wallpaper_fetcher"/>
       <traffic_annotation unique_id="wallpaper_google_photos_count"/>
     </sender>
diff --git a/ui/android/java/res/values-v17/styles.xml b/ui/android/java/res/values-v17/styles.xml
index 46440fa..51df0a0 100644
--- a/ui/android/java/res/values-v17/styles.xml
+++ b/ui/android/java/res/values-v17/styles.xml
@@ -343,6 +343,11 @@
         <item name="android:textColor">@color/chip_text_color_secondary</item>
     </style>
 
+    <!-- Toast UI -->
+    <style name="TextAppearance.Toast" parent="TextAppearance.TextSmall">
+        <item name="android:textColor">@color/default_text_color_light</item>
+    </style>
+
     <!-- Dividers -->
     <style name="HorizontalDivider"
            tools:ignore="UnusedResources">
diff --git a/ui/webui/resources/cr_components/app_management/shared_style.html b/ui/webui/resources/cr_components/app_management/shared_style.html
index e5bece1..bc0ecd20 100644
--- a/ui/webui/resources/cr_components/app_management/shared_style.html
+++ b/ui/webui/resources/cr_components/app_management/shared_style.html
@@ -1,7 +1,6 @@
 <template>
-  <style include="cr-shared-style cros-color-overrides">
+  <style include="cr-shared-style">
     .card-container {
-      background-color: var(--cros-bg-color);
       border-radius: var(--cr-card-border-radius);
       box-shadow: var(--cr-card-shadow);
       display: flex;
diff --git a/ui/webui/resources/cr_components/app_management/shared_style.js b/ui/webui/resources/cr_components/app_management/shared_style.js
index e3addf6..26d3b4ac 100644
--- a/ui/webui/resources/cr_components/app_management/shared_style.js
+++ b/ui/webui/resources/cr_components/app_management/shared_style.js
@@ -3,7 +3,6 @@
 // found in the LICENSE file.
 
 import './shared_vars.js';
-import '//resources/cr_elements/chromeos/cros_color_overrides.m.js';
 import '//resources/cr_elements/shared_style_css.m.js';
 import '//resources/cr_elements/shared_vars_css.m.js';
 
@@ -12,4 +11,4 @@
 <dom-module id="app-management-shared-css" assetpath="chrome://resources/">
 {__html_template__}</dom-module>
 `;
-document.body.appendChild(template.content.cloneNode(true));
\ No newline at end of file
+document.body.appendChild(template.content.cloneNode(true));
diff --git a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts
index 21fc89e..3b780955 100644
--- a/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts
+++ b/ui/webui/resources/cr_elements/cr_search_field/cr_search_field.d.ts
@@ -2,12 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+import {CrInputElement} from '../cr_input/cr_input.m.js';
+
 import {CrSearchFieldBehavior} from './cr_search_field_behavior.js';
 
 interface CrSearchFieldElement extends CrSearchFieldBehavior, HTMLElement {
   $: {
     clearSearch: HTMLElement,
-    searchInput: HTMLElement,
+    searchInput: CrInputElement,
   };
   autofocus: boolean;
 }
diff --git a/ui/webui/resources/cr_elements/cr_slider/cr_slider.d.ts b/ui/webui/resources/cr_elements/cr_slider/cr_slider.d.ts
index 984669e7..8d715e9 100644
--- a/ui/webui/resources/cr_elements/cr_slider/cr_slider.d.ts
+++ b/ui/webui/resources/cr_elements/cr_slider/cr_slider.d.ts
@@ -21,6 +21,7 @@
   snaps: boolean;
   ticks: Array<SliderTick>|Array<number>;
   value: number;
+  getRatio(): number;
 }
 
 export {CrSliderElement};
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.d.ts b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.d.ts
index 23f2944c..ad25d4d 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.d.ts
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.d.ts
@@ -8,6 +8,9 @@
 
 interface CrToolbarSearchFieldElement extends CrSearchFieldBehavior,
                                               LegacyElementMixin, HTMLElement {
+  $: {
+    searchInput: HTMLInputElement,
+  };
   narrow: boolean;
   showingSearch: boolean;
   autofocus: boolean;
diff --git a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
index 15dd22e2..ab80f76 100644
--- a/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
+++ b/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.js
@@ -167,7 +167,7 @@
    * @private
    */
   showSearch_(e) {
-    if (e.target !== this.$.clearSearch) {
+    if (e.target !== this.shadowRoot.querySelector('#clearSearch')) {
       this.showingSearch = true;
     }
   }
diff --git a/url/url_constants.cc b/url/url_constants.cc
index d7a5de7..96850982 100644
--- a/url/url_constants.cc
+++ b/url/url_constants.cc
@@ -7,33 +7,56 @@
 namespace url {
 
 const char kAboutBlankURL[] = "about:blank";
+const char16_t kAboutBlankURL16[] = u"about:blank";
 const char kAboutSrcdocURL[] = "about:srcdoc";
+const char16_t kAboutSrcdocURL16[] = u"about:srcdoc";
 
 const char kAboutBlankPath[] = "blank";
+const char16_t kAboutBlankPath16[] = u"blank";
 const char kAboutSrcdocPath[] = "srcdoc";
+const char16_t kAboutSrcdocPath16[] = u"srcdoc";
 
 const char kAboutScheme[] = "about";
+const char16_t kAboutScheme16[] = u"about";
 const char kBlobScheme[] = "blob";
+const char16_t kBlobScheme16[] = u"blob";
 const char kContentScheme[] = "content";
+const char16_t kContentScheme16[] = u"content";
 const char kContentIDScheme[] = "cid";
+const char16_t kContentIDScheme16[] = u"cid";
 const char kDataScheme[] = "data";
+const char16_t kDataScheme16[] = u"data";
 const char kFileScheme[] = "file";
+const char16_t kFileScheme16[] = u"file";
 const char kFileSystemScheme[] = "filesystem";
+const char16_t kFileSystemScheme16[] = u"filesystem";
 const char kFtpScheme[] = "ftp";
+const char16_t kFtpScheme16[] = u"ftp";
 const char kHttpScheme[] = "http";
+const char16_t kHttpScheme16[] = u"http";
 const char kHttpsScheme[] = "https";
+const char16_t kHttpsScheme16[] = u"https";
 const char kJavaScriptScheme[] = "javascript";
+const char16_t kJavaScriptScheme16[] = u"javascript";
 const char kMailToScheme[] = "mailto";
+const char16_t kMailToScheme16[] = u"mailto";
 // This is for QuicTransport (https://wicg.github.io/web-transport/).
 // See also: https://www.iana.org/assignments/uri-schemes/prov/quic-transport
 const char kQuicTransportScheme[] = "quic-transport";
+const char16_t kQuicTransportScheme16[] = u"quic-transport";
 const char kTelScheme[] = "tel";
+const char16_t kTelScheme16[] = u"tel";
 const char kUrnScheme[] = "urn";
+const char16_t kUrnScheme16[] = u"urn";
 const char kUuidInPackageScheme[] = "uuid-in-package";
+const char16_t kUuidInPackageScheme16[] = u"uuid-in-package";
 const char kWsScheme[] = "ws";
+const char16_t kWsScheme16[] = u"ws";
 const char kWssScheme[] = "wss";
+const char16_t kWssScheme16[] = u"wss";
 
 const char kStandardSchemeSeparator[] = "://";
+const char16_t kStandardSchemeSeparator16[] = u"://";
 
 const size_t kMaxURLChars = 2 * 1024 * 1024;
 
diff --git a/url/url_constants.h b/url/url_constants.h
index 1b5fefe..1722666 100644
--- a/url/url_constants.h
+++ b/url/url_constants.h
@@ -12,33 +12,56 @@
 namespace url {
 
 COMPONENT_EXPORT(URL) extern const char kAboutBlankURL[];
+COMPONENT_EXPORT(URL) extern const char16_t kAboutBlankURL16[];
 COMPONENT_EXPORT(URL) extern const char kAboutSrcdocURL[];
+COMPONENT_EXPORT(URL) extern const char16_t kAboutSrcdocURL16[];
 
 COMPONENT_EXPORT(URL) extern const char kAboutBlankPath[];
+COMPONENT_EXPORT(URL) extern const char16_t kAboutBlankPath16[];
 COMPONENT_EXPORT(URL) extern const char kAboutSrcdocPath[];
+COMPONENT_EXPORT(URL) extern const char16_t kAboutSrcdocPath16[];
 
 COMPONENT_EXPORT(URL) extern const char kAboutScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kAboutScheme16[];
 COMPONENT_EXPORT(URL) extern const char kBlobScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kBlobScheme16[];
 // The content scheme is specific to Android for identifying a stored file.
 COMPONENT_EXPORT(URL) extern const char kContentScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kContentScheme16[];
 COMPONENT_EXPORT(URL) extern const char kContentIDScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kContentIDScheme16[];
 COMPONENT_EXPORT(URL) extern const char kDataScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kDataScheme16[];
 COMPONENT_EXPORT(URL) extern const char kFileScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kFileScheme16[];
 COMPONENT_EXPORT(URL) extern const char kFileSystemScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kFileSystemScheme16[];
 COMPONENT_EXPORT(URL) extern const char kFtpScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kFtpScheme16[];
 COMPONENT_EXPORT(URL) extern const char kHttpScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kHttpScheme16[];
 COMPONENT_EXPORT(URL) extern const char kHttpsScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kHttpsScheme16[];
 COMPONENT_EXPORT(URL) extern const char kJavaScriptScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kJavaScriptScheme16[];
 COMPONENT_EXPORT(URL) extern const char kMailToScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kMailToScheme16[];
 COMPONENT_EXPORT(URL) extern const char kQuicTransportScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kQuicTransportScheme16[];
 COMPONENT_EXPORT(URL) extern const char kTelScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kTelScheme16[];
 COMPONENT_EXPORT(URL) extern const char kUrnScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kUrnScheme16[];
 COMPONENT_EXPORT(URL) extern const char kUuidInPackageScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kUuidInPackageScheme16[];
 COMPONENT_EXPORT(URL) extern const char kWsScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kWsScheme16[];
 COMPONENT_EXPORT(URL) extern const char kWssScheme[];
+COMPONENT_EXPORT(URL) extern const char16_t kWssScheme16[];
 
 // Used to separate a standard scheme and the hostname: "://".
 COMPONENT_EXPORT(URL) extern const char kStandardSchemeSeparator[];
+COMPONENT_EXPORT(URL) extern const char16_t kStandardSchemeSeparator16[];
 
 COMPONENT_EXPORT(URL) extern const size_t kMaxURLChars;