diff --git a/DEPS b/DEPS
index e6e9748e..bac8e56 100644
--- a/DEPS
+++ b/DEPS
@@ -167,11 +167,11 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': '69537d79899407c4f4a5882a793b46fbf7f87256',
+  'skia_revision': '2d8a95ee71b5a459eb7dfa26400bfd089e7b268a',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
-  'v8_revision': 'c2dc845a0ba81912c7356178de67124da98f13ff',
+  'v8_revision': '5697a8371da8791c2f8a2a1177d0349b537d4665',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling swarming_client
   # and whatever else without interference from each other.
@@ -179,7 +179,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': 'ad5f71649e210069ab5427ddb46755358df220a1',
+  'angle_revision': 'd200a77a22cf4241ab0e5714f4479ca300334bf1',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
@@ -187,7 +187,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': '74dbc8af75f9dc2490e498f3de57c84edb2fb4fe',
+  'pdfium_revision': '9fa72471b87de2f4db6d13097519e6cc854e7620',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling BoringSSL
   # and whatever else without interference from each other.
@@ -230,7 +230,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling catapult
   # and whatever else without interference from each other.
-  'catapult_revision': 'df24b8a3609962fa2f491d52b8d68ece5a585715',
+  'catapult_revision': '51c8a7860a5a92b0ab57c8a1d092d3bf523fa727',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling libFuzzer
   # and whatever else without interference from each other.
@@ -302,7 +302,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': '91b2142ee44d3e4eb3bafcbc8eec4d196006c9d3',
+  'dawn_revision': '9e64afcb91f74e75a16fa5639d272c5c2a536bc6',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -862,7 +862,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '8e881f105660fce901e238c4926008c24874eb16',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '1f67cf3bf610fc9b1d33d693c326e1071a7a2496',
       'condition': 'checkout_linux',
   },
 
@@ -1280,7 +1280,7 @@
   },
 
   'src/third_party/perfetto':
-    Var('android_git') + '/platform/external/perfetto.git' + '@' + 'e6b95e45544dd0ec3e57e21b2aec9a25bd7c664b',
+    Var('android_git') + '/platform/external/perfetto.git' + '@' + '14bf0b9d81222e3c379f94c73180b6505eef892f',
 
   'src/third_party/perl': {
       'url': Var('chromium_git') + '/chromium/deps/perl.git' + '@' + '6f3e5028eb65d0b4c5fdd792106ac4c84eee1eb3',
@@ -1470,7 +1470,7 @@
     Var('chromium_git') + '/external/khronosgroup/webgl.git' + '@' + '2701c130839edbeb226735b0775966b6423d9e83',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '03f4b36bdd9a04d777ccbe526310448d8446cc4e',
+    Var('webrtc_git') + '/src.git' + '@' + 'f17976d01993391c002d7a333bd1d882f2741180',
 
   'src/third_party/xdg-utils': {
       'url': Var('chromium_git') + '/chromium/deps/xdg-utils.git' + '@' + 'd80274d5869b17b8c9067a1022e4416ee7ed5e0d',
@@ -1532,7 +1532,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@01cfdcb21734ff15dff696551b7cf4c6714e0959',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@b21973d2c0c9f937332eee787d6d14df96288ce8',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/android_webview/BUILD.gn b/android_webview/BUILD.gn
index 38a04803..23a51d4 100644
--- a/android_webview/BUILD.gn
+++ b/android_webview/BUILD.gn
@@ -40,7 +40,6 @@
 
 generate_jni("browser_jni_headers") {
   sources = [
-    "apk/java/src/com/android/webview/chromium/WebViewApkApplication.java",
     "java/src/org/chromium/android_webview/AndroidProtocolHandler.java",
     "java/src/org/chromium/android_webview/AwAutofillClient.java",
     "java/src/org/chromium/android_webview/AwBrowserContext.java",
@@ -458,7 +457,6 @@
 
 source_set("common") {
   sources = [
-    "apk/webview_apk_application.cc",
     "lib/aw_main_delegate.cc",
     "lib/aw_main_delegate.h",
     "lib/webview_jni_onload.cc",
@@ -467,14 +465,13 @@
 
   deps = [
     ":browser_jni_headers",
+    "//android_webview/apk",
+    "//android_webview/apk:webview_license_provider",
     "//android_webview/browser",
     "//android_webview/browser/gfx",
     "//android_webview/common",
     "//android_webview/gpu",
     "//android_webview/renderer",
-
-    # Called via JNI
-    "//components/about_ui",
     "//components/autofill/core/common",
     "//components/crash/android:crashpad_main",
     "//components/gwp_asan/buildflags",
@@ -832,7 +829,6 @@
     ":common_platform_services_java",
     ":common_variations_utils_java",
     ":system_webview_manifest",
-    "//android_webview/apk:apk_java",
     "//base:base_java",
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/minidump_uploader:minidump_uploader_java",
diff --git a/android_webview/apk/BUILD.gn b/android_webview/apk/BUILD.gn
index bc42c2b..cc44b70 100644
--- a/android_webview/apk/BUILD.gn
+++ b/android_webview/apk/BUILD.gn
@@ -4,6 +4,12 @@
 
 import("//build/config/android/rules.gni")
 
+generate_jni("apk_jni_headers") {
+  sources = [
+    "java/src/com/android/webview/chromium/WebViewApkApplication.java",
+  ]
+}
+
 # Monochrome uses a different copy of LicenseContentProvider.java.
 # This one is used in Trichrome and SystemWebview.
 android_library("webview_license_provider_java") {
@@ -16,10 +22,12 @@
   ]
 }
 
-# TODO(agrieve): Delete once downstream reference is updated.
-java_group("webview_license_activity_java") {
+# Contains the native dependencies used by LicenseContentProvider via JNI.
+# This should be included in the shared library of any APK that depends on
+# :webview_license_provider_java.
+group("webview_license_provider") {
   deps = [
-    ":apk_java",
+    "//components/about_ui",
   ]
 }
 
@@ -40,3 +48,14 @@
   ]
   annotation_processor_deps = [ "//base/android/jni_generator:jni_processor" ]
 }
+
+source_set("apk") {
+  sources = [
+    "webview_apk_application.cc",
+  ]
+  deps = [
+    ":apk_jni_headers",
+    "//android_webview/common",
+    "//base",
+  ]
+}
diff --git a/android_webview/apk/webview_apk_application.cc b/android_webview/apk/webview_apk_application.cc
index ff5756c..41f6eb7d 100644
--- a/android_webview/apk/webview_apk_application.cc
+++ b/android_webview/apk/webview_apk_application.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "android_webview/browser_jni_headers/WebViewApkApplication_jni.h"
+#include "android_webview/apk/apk_jni_headers/WebViewApkApplication_jni.h"
 
 #include "android_webview/common/aw_resource_bundle.h"
 #include "base/android/base_jni_onload.h"
diff --git a/android_webview/browser/cookie_manager.cc b/android_webview/browser/cookie_manager.cc
index 7176279..5cb14e7 100644
--- a/android_webview/browser/cookie_manager.cc
+++ b/android_webview/browser/cookie_manager.cc
@@ -405,9 +405,6 @@
 void CookieManager::SetCookieHelper(const GURL& host,
                                     const std::string& value,
                                     base::OnceCallback<void(bool)> callback) {
-  net::CookieOptions options;
-  options.set_include_httponly();
-
   const GURL& new_host = MaybeFixUpSchemeForSecureCookie(host, value);
 
   net::CanonicalCookie::CookieInclusionStatus status;
@@ -429,12 +426,13 @@
     // *cc.get() is safe, because network::CookieManager::SetCanonicalCookie
     // will make a copy before our smart pointer goes out of scope.
     GetMojoCookieManager()->SetCanonicalCookie(
-        *cc.get(), new_host.scheme(), options,
+        *cc.get(), new_host.scheme(), net::CookieOptions::MakeAllInclusive(),
         net::cookie_util::AdaptCookieInclusionStatusToBool(
             std::move(callback)));
   } else {
     GetCookieStore()->SetCanonicalCookieAsync(
-        std::move(cc), new_host.scheme(), options,
+        std::move(cc), new_host.scheme(),
+        net::CookieOptions::MakeAllInclusive(),
         net::cookie_util::AdaptCookieInclusionStatusToBool(
             std::move(callback)));
   }
@@ -458,10 +456,7 @@
 void CookieManager::GetCookieListAsyncHelper(const GURL& host,
                                              net::CookieList* result,
                                              base::OnceClosure complete) {
-  net::CookieOptions options;
-  options.set_include_httponly();
-  options.set_same_site_cookie_context(
-      net::CookieOptions::SameSiteCookieContext::SAME_SITE_STRICT);
+  net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
 
   if (GetMojoCookieManager()) {
     GetMojoCookieManager()->GetCookieList(
diff --git a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
index d9bcff1..b089bdc 100644
--- a/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
+++ b/android_webview/javatests/src/org/chromium/android_webview/test/CookieManagerTest.java
@@ -325,6 +325,16 @@
     @Test
     @MediumTest
     @Feature({"AndroidWebView", "Privacy"})
+    public void testSetCookieSameSite() {
+        String url = "http://www.example.com";
+        String cookie = "name=test";
+        mCookieManager.setCookie(url, cookie + "; SameSite=Lax");
+        assertCookieEquals(cookie, url);
+    }
+
+    @Test
+    @MediumTest
+    @Feature({"AndroidWebView", "Privacy"})
     public void testSetCookieWithDomainForUrl() {
         // If the app passes ".www.example.com" or "http://.www.example.com", the glue layer "fixes"
         // this to "http:///.www.example.com"
diff --git a/android_webview/support_library/boundary_interfaces/build.gradle b/android_webview/support_library/boundary_interfaces/build.gradle
index 300ab6c..6bf1a277 100644
--- a/android_webview/support_library/boundary_interfaces/build.gradle
+++ b/android_webview/support_library/boundary_interfaces/build.gradle
@@ -8,6 +8,7 @@
 import androidx.build.SupportConfig
 
 plugins {
+    id('AndroidXPlugin')
     id('com.android.library')
 }
 
diff --git a/ash/app_list/views/search_result_tile_item_list_view.cc b/ash/app_list/views/search_result_tile_item_list_view.cc
index c92974d..6b8cbb6 100644
--- a/ash/app_list/views/search_result_tile_item_list_view.cc
+++ b/ash/app_list/views/search_result_tile_item_list_view.cc
@@ -81,33 +81,33 @@
           app_list_features::IsAppReinstallZeroStateEnabled()),
       max_search_result_tiles_(
           AppListConfig::instance().max_search_result_tiles()) {
-  SetLayoutManager(std::make_unique<views::BoxLayout>(
+  layout_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
       views::BoxLayout::Orientation::kHorizontal,
       gfx::Insets(kItemListVerticalSpacing, kItemListHorizontalSpacing),
       kBetweenItemSpacing));
   for (size_t i = 0; i < max_search_result_tiles_; ++i) {
     if (is_app_reinstall_recommendation_enabled_ ||
         is_play_store_app_search_enabled_) {
-      views::Separator* separator = new views::Separator;
+      views::Separator* separator =
+          AddChildView(std::make_unique<views::Separator>());
       separator->SetVisible(false);
       separator->SetBorder(views::CreateEmptyBorder(
           kSeparatorTopPadding, kSeparatorLeftRightPadding,
           AppListConfig::instance().search_tile_height() - kSeparatorHeight,
           kSeparatorLeftRightPadding));
       separator->SetColor(kSeparatorColor);
-
       separator_views_.push_back(separator);
-      AddChildView(separator);
+      layout_->SetFlexForView(separator, 0);
     }
 
-    SearchResultTileItemView* tile_item = new SearchResultTileItemView(
-        view_delegate, nullptr /* pagination model */,
-        false /* show_in_apps_page */);
+    SearchResultTileItemView* tile_item =
+        AddChildView(std::make_unique<SearchResultTileItemView>(
+            view_delegate, nullptr /* pagination model */,
+            false /* show_in_apps_page */));
     tile_item->set_index_in_container(i);
     tile_item->SetParentBackgroundColor(
         AppListConfig::instance().card_background_color());
     tile_views_.push_back(tile_item);
-    AddChildView(tile_item);
     AddObservedResultView(tile_item);
   }
 
@@ -390,6 +390,14 @@
   return "SearchResultTileItemListView";
 }
 
+void SearchResultTileItemListView::Layout() {
+  const bool flex = GetContentsBounds().width() < GetPreferredSize().width();
+  layout_->SetDefaultFlex(flex ? 1 : 0);
+  layout_->set_between_child_spacing(flex ? 1 : kBetweenItemSpacing);
+
+  views::View::Layout();
+}
+
 void SearchResultTileItemListView::OnShownChanged() {
   SearchResultContainerView::OnShownChanged();
   for (const auto* tile_view : tile_views_) {
diff --git a/ash/app_list/views/search_result_tile_item_list_view.h b/ash/app_list/views/search_result_tile_item_list_view.h
index d079c9c..8fd4c0f 100644
--- a/ash/app_list/views/search_result_tile_item_list_view.h
+++ b/ash/app_list/views/search_result_tile_item_list_view.h
@@ -13,8 +13,9 @@
 #include "base/timer/timer.h"
 
 namespace views {
-class Textfield;
+class BoxLayout;
 class Separator;
+class Textfield;
 }  // namespace views
 
 namespace ash {
@@ -40,6 +41,7 @@
   // Overridden from views::View:
   bool OnKeyPressed(const ui::KeyEvent& event) override;
   const char* GetClassName() const override;
+  void Layout() override;
 
   const std::vector<SearchResultTileItemView*>& tile_views_for_test() const {
     return tile_views_;
@@ -71,8 +73,9 @@
   std::vector<views::Separator*> separator_views_;
 
   // Owned by the views hierarchy.
-  SearchResultPageView* const search_result_page_view_;
-  views::Textfield* search_box_;
+  SearchResultPageView* const search_result_page_view_ = nullptr;
+  views::Textfield* search_box_ = nullptr;
+  views::BoxLayout* layout_ = nullptr;
 
   base::string16 recent_playstore_query_;
 
diff --git a/ash/shelf/shelf_config.cc b/ash/shelf/shelf_config.cc
index 6e0284b..3f334e41 100644
--- a/ash/shelf/shelf_config.cc
+++ b/ash/shelf/shelf_config.cc
@@ -179,10 +179,7 @@
   if (!IsTabletMode())
     return 36;
 
-  if (is_dense_)
-    return is_in_app() ? 28 : 36;
-  else
-    return is_in_app() ? 30 : 40;
+  return is_dense_ ? 36 : 40;
 }
 
 int ShelfConfig::control_border_radius() const {
diff --git a/ash/shelf/shelf_config_unittest.cc b/ash/shelf/shelf_config_unittest.cc
index 317abda..8a7208d8 100644
--- a/ash/shelf/shelf_config_unittest.cc
+++ b/ash/shelf/shelf_config_unittest.cc
@@ -87,13 +87,17 @@
 // Make sure that the shelf size changes when switching between contexts:
 // home vs in-app, clamshell vs tablet, dense vs not dense.
 TEST_F(ShelfConfigTest, ShelfSizeChangesWithContext) {
-  // For both the shelf size and the shelf control button size, the ordering
-  // should be as follows, in increasing order:
+  // For shelf size, the ordering should be as follows, in increasing order:
   // * Tablet, dense, in-app
   // * Tablet, standard, in-app
   // * Tablet, dense, home == Clamshell (all modes)
   // * Tablet, standard, home
 
+  // For shelf control button size, the ordering should be as follows, in
+  // increasing order:
+  // * Tablet, dense (in-app and home) == Clamshell (all modes)
+  // * Tablet, standard (in-app and home)
+
   UpdateDisplay("300x1000");
   SetTabletMode(true);
   ASSERT_TRUE(IsTabletMode());
@@ -136,11 +140,11 @@
   EXPECT_EQ(clamshell_home, clamshell_in_app);
   EXPECT_LT(tablet_dense_home, tablet_standard_home);
 
-  EXPECT_LT(control_tablet_dense_in_app, control_tablet_standard_in_app);
-  EXPECT_LT(control_tablet_standard_in_app, control_tablet_dense_home);
-  EXPECT_EQ(control_tablet_dense_home, control_clamshell_home);
-  EXPECT_EQ(control_clamshell_home, control_clamshell_in_app);
-  EXPECT_LT(control_tablet_dense_home, control_tablet_standard_home);
+  EXPECT_EQ(control_tablet_dense_in_app, control_tablet_dense_home);
+  EXPECT_EQ(control_tablet_dense_home, control_clamshell_in_app);
+  EXPECT_EQ(control_clamshell_in_app, control_clamshell_home);
+  EXPECT_LT(control_clamshell_home, control_tablet_standard_in_app);
+  EXPECT_EQ(control_tablet_standard_in_app, control_tablet_standard_home);
 }
 
 // Make sure that we consider ourselves inside an app when appropriate.
diff --git a/ash/shelf/shelf_navigation_widget.cc b/ash/shelf/shelf_navigation_widget.cc
index 04a2626..a021406 100644
--- a/ash/shelf/shelf_navigation_widget.cc
+++ b/ash/shelf/shelf_navigation_widget.cc
@@ -33,6 +33,13 @@
          Shell::Get()->tablet_mode_controller()->InTabletMode();
 }
 
+// Returns the desired spacing between the back and home buttons.
+int GetButtonSpacing() {
+  if (IsTabletMode() && ShelfConfig::Get()->is_in_app())
+    return 0;
+  return ShelfConfig::Get()->button_spacing();
+}
+
 // Returns the bounds for the first button shown in this view (the back
 // button in tablet mode, the home button otherwise).
 gfx::Rect GetFirstButtonBounds() {
@@ -44,10 +51,9 @@
 // always the home button and only in tablet mode, which implies a horizontal
 // shelf).
 gfx::Rect GetSecondButtonBounds() {
-  return gfx::Rect(
-      ShelfConfig::Get()->control_size() + ShelfConfig::Get()->button_spacing(),
-      0, ShelfConfig::Get()->control_size(),
-      ShelfConfig::Get()->control_size());
+  return gfx::Rect(ShelfConfig::Get()->control_size() + GetButtonSpacing(), 0,
+                   ShelfConfig::Get()->control_size(),
+                   ShelfConfig::Get()->control_size());
 }
 
 }  // namespace
@@ -229,8 +235,7 @@
   if (!shelf_->IsHorizontalAlignment())
     return gfx::Size(control_size, control_size);
   return gfx::Size(
-      IsTabletMode() ? (2 * control_size + ShelfConfig::Get()->button_spacing())
-                     : control_size,
+      IsTabletMode() ? (2 * control_size + GetButtonSpacing()) : control_size,
       control_size);
 }
 
diff --git a/ash/shelf/shelf_view.cc b/ash/shelf/shelf_view.cc
index e26ff51..1927c1f 100644
--- a/ash/shelf/shelf_view.cc
+++ b/ash/shelf/shelf_view.cc
@@ -2204,6 +2204,11 @@
         view, std::unique_ptr<gfx::AnimationDelegate>(
                   new FadeOutAnimationDelegate(this, view)));
   } else {
+    // If there is no fade out animation, notify the parent view of the
+    // changed size before bounds animations start.
+    if (chromeos::switches::ShouldShowScrollableShelf())
+      PreferredSizeChanged();
+
     // We don't need to show a fade out animation for invisible |view|. When an
     // item is ripped out from the shelf, its |view| is already invisible.
     AnimateToIdealBounds();
diff --git a/ash/wm/splitview/split_view_drag_indicators_unittest.cc b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
index ea2f071a..fee3fe3 100644
--- a/ash/wm/splitview/split_view_drag_indicators_unittest.cc
+++ b/ash/wm/splitview/split_view_drag_indicators_unittest.cc
@@ -393,6 +393,40 @@
   check_helper(indicator.get(), to_int(IndicatorType::kLeftHighlight));
   indicator->SetIndicatorState(IndicatorState::kPreviewAreaRight, gfx::Point());
   check_helper(indicator.get(), to_int(IndicatorType::kRightHighlight));
+
+  const int left_with_text =
+      to_int(IndicatorType::kLeftHighlight) | to_int(IndicatorType::kLeftText);
+  const int right_with_text = to_int(IndicatorType::kRightHighlight) |
+                              to_int(IndicatorType::kRightText);
+  // Verify that only one highlight shows up for each one-sided dragging or
+  // cannot snap state, if the previous state is |IndicatorState::kNone|.
+  indicator->SetIndicatorState(IndicatorState::kNone, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kDragAreaLeft, gfx::Point());
+  check_helper(indicator.get(), left_with_text);
+  indicator->SetIndicatorState(IndicatorState::kNone, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kDragAreaRight, gfx::Point());
+  check_helper(indicator.get(), right_with_text);
+  indicator->SetIndicatorState(IndicatorState::kNone, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kCannotSnapLeft, gfx::Point());
+  check_helper(indicator.get(), left_with_text);
+  indicator->SetIndicatorState(IndicatorState::kNone, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kCannotSnapRight, gfx::Point());
+  check_helper(indicator.get(), right_with_text);
+
+  // Verify that only one highlight shows up for each one-sided dragging or
+  // cannot snap state, if the previous state is of a preview on the same side.
+  indicator->SetIndicatorState(IndicatorState::kPreviewAreaLeft, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kDragAreaLeft, gfx::Point());
+  check_helper(indicator.get(), left_with_text);
+  indicator->SetIndicatorState(IndicatorState::kPreviewAreaRight, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kDragAreaRight, gfx::Point());
+  check_helper(indicator.get(), right_with_text);
+  indicator->SetIndicatorState(IndicatorState::kPreviewAreaLeft, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kCannotSnapLeft, gfx::Point());
+  check_helper(indicator.get(), left_with_text);
+  indicator->SetIndicatorState(IndicatorState::kPreviewAreaRight, gfx::Point());
+  indicator->SetIndicatorState(IndicatorState::kCannotSnapRight, gfx::Point());
+  check_helper(indicator.get(), right_with_text);
 }
 
 // Verify that the split view drag indicators widget reparents when starting a
diff --git a/ash/wm/splitview/split_view_highlight_view_unittest.cc b/ash/wm/splitview/split_view_highlight_view_unittest.cc
index 848a68821..c7623a9 100644
--- a/ash/wm/splitview/split_view_highlight_view_unittest.cc
+++ b/ash/wm/splitview/split_view_highlight_view_unittest.cc
@@ -207,40 +207,4 @@
   EXPECT_TRUE(GetTransform(test_api.GetRightBottomView()).IsIdentity());
 }
 
-// Test when there is only one indicator is visible, the other indicator should
-// always have 0 opacity.
-TEST_F(SplitViewHighlightViewTest, SingleIndicatorOpacityTest) {
-  const gfx::Rect bounds(100, 0, 100, 100);
-  left_highlight()->SetBounds(bounds, /*landscape=*/false,
-                              /*animation_type=*/base::nullopt);
-  left_highlight()->SetPaintToLayer();
-  left_highlight()->layer()->SetOpacity(0.f);
-  right_highlight()->SetBounds(bounds, /*landscape=*/false,
-                               /*animation_type=*/base::nullopt);
-  right_highlight()->SetPaintToLayer();
-  right_highlight()->layer()->SetOpacity(0.f);
-
-  left_highlight()->OnIndicatorTypeChanged(IndicatorState::kDragAreaRight,
-                                           IndicatorState::kNone);
-  EXPECT_EQ(0.f, left_highlight()->layer()->opacity());
-  right_highlight()->OnIndicatorTypeChanged(IndicatorState::kDragAreaRight,
-                                            IndicatorState::kNone);
-  EXPECT_EQ(kHighlightOpacity, right_highlight()->layer()->opacity());
-
-  left_highlight()->OnIndicatorTypeChanged(IndicatorState::kPreviewAreaRight,
-                                           IndicatorState::kDragAreaRight);
-  EXPECT_EQ(0.f, left_highlight()->layer()->opacity());
-  right_highlight()->OnIndicatorTypeChanged(IndicatorState::kPreviewAreaRight,
-                                            IndicatorState::kDragAreaRight);
-  EXPECT_EQ(kPreviewAreaHighlightOpacity,
-            right_highlight()->layer()->opacity());
-
-  left_highlight()->OnIndicatorTypeChanged(IndicatorState::kDragAreaRight,
-                                           IndicatorState::kPreviewAreaRight);
-  EXPECT_EQ(0.f, left_highlight()->layer()->opacity());
-  right_highlight()->OnIndicatorTypeChanged(IndicatorState::kDragAreaRight,
-                                            IndicatorState::kPreviewAreaRight);
-  EXPECT_EQ(kHighlightOpacity, right_highlight()->layer()->opacity());
-}
-
 }  // namespace ash
diff --git a/build/android/docs/README.md b/build/android/docs/README.md
index b6f0a6e..404ca11 100644
--- a/build/android/docs/README.md
+++ b/build/android/docs/README.md
@@ -2,6 +2,7 @@
 
 * [android_app_bundles.md](android_app_bundles.md)
 * [build_config.md](build_config.md)
+* [building_dex.md](building_dex.md)
 * [coverage.md](coverage.md)
 * [lint.md](lint.md)
 * [life_of_a_resource.md](life_of_a_resource.md)
diff --git a/build/android/docs/building_dex.md b/build/android/docs/building_dex.md
new file mode 100644
index 0000000..e671937
--- /dev/null
+++ b/build/android/docs/building_dex.md
@@ -0,0 +1,140 @@
+# Building Dex
+
+This doc aims to describe the Chrome build process that takes a set of `.java`
+files and turns them into a `classes.dex` file.
+
+[TOC]
+
+## Core GN Target Types
+
+The following have `supports_android` and `requires_android` set to false by
+default:
+* `java_library()`: Compiles `.java` -> `.jar`
+* `java_prebuilt()`:  Imports a prebuilt `.jar` file.
+
+The following have `supports_android` and `requires_android` set to true. They
+also have a default `jar_excluded_patterns` set (more on that later):
+* `android_library()`
+* `android_java_prebuilt()`
+
+All targets names must end with "_java" so that the build system can distinguish
+them from non-java targets (or [other variations](https://cs.chromium.org/chromium/src/build/config/android/internal_rules.gni?rcl=ec2c17d7b4e424e060c3c7972842af87343526a1&l=20)).
+
+## Step 1: Compile
+
+This step is the only step that does not apply to prebuilt targets.
+
+* All `.java` files in a target are compiled by `javac` into `.class` files.
+  * This includes `.java` files that live within `.srcjar` files, referenced
+    through `srcjar_deps`.
+* The `classpath` used when compiling a target is comprised of `.jar` files of
+  its deps.
+  * When deps are library targets, the Step 1 `.jar` file is used.
+  * When deps are prebuilt targets, the original `.jar` file is used.
+  * All `.jar` processing done in subsequent steps does not impact compilation
+    classpath.
+* `.class` files are zipped into an output `.jar` file.
+* There is **no support** for incremental compilation at this level.
+  * If one source file changes within a library, then the entire library is
+    recompiled.
+  * Prefer smaller targets to avoid slow compiles.
+
+## Step 2: Creating an .interface.jar
+
+This step happens in parallel with subsequent steps.
+
+* `//third_party/ijar` converts the `.jar` into an `.interface.jar`, which is a
+  copy of the input with all non-public symbols and function bodies removed.
+* Dependant targets use `.interface.jar` files to skip having to be rebuilt
+  when only private implementation details change.
+
+## Step 3: Bytecode Processing
+
+* `//build/android/bytecode` runs on the compiled `.jar` in order to:
+  * Enable Java assertions (when dcheck is enabled).
+  * Assert that libraries have properly declared `deps`.
+
+## Step 4: Desugaring
+
+This step happens only when targets have `supports_android = true`.
+
+* `//third_party/bazel/desugar` converts certain Java 8 constructs, such as
+  lambdas and default interface methods, into constructs that are compatible
+  with Java 7.
+
+## Step 5: Filtering
+
+This step happens only when targets that have `jar_excluded_patterns` or
+`jar_included_patterns` set (e.g. all `android_` targets).
+
+* Remove `.class` files that match the filters from the `.jar`. These `.class`
+  files are generally those that are re-created with different implementations
+  further on in the build process.
+  * E.g.: `R.class` files - a part of [Android Resources].
+  * E.g.: `GEN_JNI.class` - a part of our [JNI] glue.
+  * E.g.: `AppHooksImpl.class` - how `chrome_java` wires up different
+    implementations for [non-public builds][apphooks].
+
+[JNI]: /base/android/jni_generator/README.md
+[Android Resources]: life_of_a_resource.md
+[apphooks]: /chrome/android/java/src/org/chromium/chrome/browser/AppHooksImpl.java
+
+## Step 6: Instrumentation
+
+This step happens only when this GN arg is set: `use_jacoco_coverage = true`
+
+* [Jacoco] adds instrumentation hooks to methods.
+
+[Jacoco]: https://www.eclemma.org/jacoco/
+
+## Step 7: Copy to lib.java
+
+* The `.jar` is copied into `$root_build_dir/lib.java` (under target-specific
+  subdirectories) so that it will be included by bot archive steps.
+  * These `.jar` files are the ones used when running `java_binary` and
+    `junit_binary` targets.
+
+## Step 8: Per-Library Dexing
+
+This step happens only when targets have `supports_android = true`.
+
+* [d8] converts `.jar` files contain `.class` files into `.dex.jar` files
+  containing `.dex` files.
+* Dexing is incremental - it will reuse dex'ed classes from a previous build if
+  the corresponding `.class` file is unchanged.
+* These per-library `.dex.jar` files are used directly by [incremental install],
+  and are inputs to the Apk step when `enable_proguard = false`.
+  * Even when `is_java_debug = false`, many apk targets do not enable ProGuard
+    (e.g. unit tests).
+
+[d8]: https://developer.android.com/studio/command-line/d8
+[incremental install]: /build/android/incremental_install/README.md
+
+## Step 9: Apk / Bundle Module Compile
+
+* Each `android_apk` and `android_bundle_module` template has a nested
+  `java_library` target. The nested library includes final copies of files
+  stripped out by prior filtering steps. These files include:
+  * Final `R.java` files, created by `compile_resources.py`.
+  * Final `GEN_JNI.java` for JNI glue.
+  * `BuildConfig.java` and `NativeLibraries.java` (//base dependencies).
+
+## Step 10: Final Dexing
+
+When `is_java_debug = true`:
+* [d8] merges all library `.dex.jar` files into a final `.dex.zip`.
+
+When `is_java_debug = false`:
+* [R8] performs whole-program optimization on all library `lib.java` `.jar`
+  files and outputs a final `.dex.zip`.
+  * For App Bundles, R8 creates a single `.dex.zip` with the code from all
+    modules.
+
+[R8]: https://r8.googlesource.com/r8
+
+## Step 11: Bundle Module Dex Splitting
+
+This step happens only when `is_java_debug = false`.
+
+* [dexsplitter.py] splits the single `.dex.zip` into per-module `.dex.zip`
+  files.
diff --git a/build/android/pylib/local/device/local_device_gtest_run.py b/build/android/pylib/local/device/local_device_gtest_run.py
index 5044cdf1..605b826 100644
--- a/build/android/pylib/local/device/local_device_gtest_run.py
+++ b/build/android/pylib/local/device/local_device_gtest_run.py
@@ -10,11 +10,13 @@
 import shutil
 import time
 
+from devil import base_error
 from devil.android import crash_handler
 from devil.android import device_errors
 from devil.android import device_temp_file
 from devil.android import logcat_monitor
 from devil.android import ports
+from devil.android.sdk import version_codes
 from devil.utils import reraiser_thread
 from incremental_install import installer
 from pylib import constants
@@ -35,6 +37,8 @@
     'org.chromium.native_test.NativeTest.CommandLineFile')
 _EXTRA_COMMAND_LINE_FLAGS = (
     'org.chromium.native_test.NativeTest.CommandLineFlags')
+_EXTRA_COVERAGE_DEVICE_FILE = (
+    'org.chromium.native_test.NativeTest.CoverageDeviceFile')
 _EXTRA_STDOUT_FILE = (
     'org.chromium.native_test.NativeTestInstrumentationTestRunner'
         '.StdoutFile')
@@ -102,6 +106,24 @@
   return patterns
 
 
+def _PullCoverageFile(device, coverage_device_file, output_dir):
+  """Pulls coverage file on device to host directory.
+
+  Args:
+    device: The working device.
+    coverage_device_file: The temporary coverage file on device.
+    output_dir: The output directory on host.
+  """
+  try:
+    if not os.path.exists(output_dir):
+      os.makedirs(output_dir)
+    device.PullFile(coverage_device_file.name, output_dir)
+  except (OSError, base_error.BaseError) as e:
+    logging.warning('Failed to handle coverage data after tests: %s', e)
+  finally:
+    coverage_device_file.close()
+
+
 class _ApkDelegate(object):
   def __init__(self, test_instance, tool):
     self._activity = test_instance.activity
@@ -116,6 +138,7 @@
     self._extras = test_instance.extras
     self._wait_for_java_debugger = test_instance.wait_for_java_debugger
     self._tool = tool
+    self._coverage_dir = test_instance.coverage_dir
 
   def GetTestDataRoot(self, device):
     # pylint: disable=no-self-use
@@ -138,6 +161,15 @@
 
   def Run(self, test, device, flags=None, **kwargs):
     extras = dict(self._extras)
+    device_api = device.build_version_sdk
+
+    if self._coverage_dir and device_api >= version_codes.LOLLIPOP:
+      coverage_device_file = device_temp_file.DeviceTempFile(
+          device.adb,
+          suffix='.profraw',
+          prefix=self._suite,
+          dir=device.GetExternalStoragePath())
+      extras[_EXTRA_COVERAGE_DEVICE_FILE] = coverage_device_file.name
 
     if ('timeout' in kwargs
         and gtest_test_instance.EXTRA_SHARD_NANO_TIMEOUT not in extras):
@@ -193,6 +225,10 @@
       except Exception:
         device.ForceStop(self._package)
         raise
+      finally:
+        if self._coverage_dir and device_api >= version_codes.LOLLIPOP:
+          _PullCoverageFile(device, coverage_device_file, self._coverage_dir)
+
       # TODO(jbudorick): Remove this after resolving crbug.com/726880
       if device.PathExists(stdout_file.name):
         logging.info('%s size on device: %s', stdout_file.name,
@@ -218,13 +254,18 @@
 
 
 class _ExeDelegate(object):
-  def __init__(self, tr, dist_dir, tool):
-    self._host_dist_dir = dist_dir
-    self._exe_file_name = os.path.basename(dist_dir)[:-len('__dist')]
+
+  def __init__(self, tr, test_instance, tool):
+    self._host_dist_dir = test_instance.exe_dist_dir
+    self._exe_file_name = os.path.basename(
+        test_instance.exe_dist_dir)[:-len('__dist')]
     self._device_dist_dir = posixpath.join(
-        constants.TEST_EXECUTABLE_DIR, os.path.basename(dist_dir))
+        constants.TEST_EXECUTABLE_DIR,
+        os.path.basename(test_instance.exe_dist_dir))
     self._test_run = tr
     self._tool = tool
+    self._coverage_dir = test_instance.coverage_dir
+    self._suite = test_instance.suite
 
   def GetTestDataRoot(self, device):
     # pylint: disable=no-self-use
@@ -261,6 +302,14 @@
       'LD_LIBRARY_PATH': self._device_dist_dir
     }
 
+    if self._coverage_dir:
+      coverage_device_file = device_temp_file.DeviceTempFile(
+          device.adb,
+          suffix='.profraw',
+          prefix=self._suite,
+          dir=device.GetExternalStoragePath())
+      env['LLVM_PROFILE_FILE'] = coverage_device_file.name
+
     if self._tool != 'asan':
       env['UBSAN_OPTIONS'] = constants.UBSAN_OPTIONS
 
@@ -276,6 +325,10 @@
     # fine from the test runner's perspective; thus check_return=False.
     output = device.RunShellCommand(
         cmd, cwd=cwd, env=env, check_return=False, large_output=True, **kwargs)
+
+    if self._coverage_dir:
+      _PullCoverageFile(device, coverage_device_file, self._coverage_dir)
+
     return output
 
   def PullAppFiles(self, device, files, directory):
@@ -296,8 +349,7 @@
     if self._test_instance.apk:
       self._delegate = _ApkDelegate(self._test_instance, env.tool)
     elif self._test_instance.exe_dist_dir:
-      self._delegate = _ExeDelegate(self, self._test_instance.exe_dist_dir,
-                                    self._env.tool)
+      self._delegate = _ExeDelegate(self, self._test_instance, self._env.tool)
     if self._test_instance.isolated_script_test_perf_output:
       self._test_perf_output_filenames = _GenerateSequentialFileNames(
           self._test_instance.isolated_script_test_perf_output)
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index 4fb0d82..268d1799 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -603,13 +603,12 @@
       def handle_coverage_data():
         if self._test_instance.coverage_directory:
           try:
+            if not os.path.exists(self._test_instance.coverage_directory):
+              os.makedirs(self._test_instance.coverage_directory)
             device.PullFile(coverage_device_file,
                             self._test_instance.coverage_directory)
-            device.RunShellCommand(
-                'rm -f %s' % posixpath.join(coverage_directory, '*'),
-                check_return=True,
-                shell=True)
-          except base_error.BaseError as e:
+            device.RemovePath(coverage_device_file, True)
+          except (OSError, base_error.BaseError) as e:
             logging.warning('Failed to handle coverage data after tests: %s', e)
 
       def handle_render_test_data():
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index fb980128..c94ed05 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -665,6 +665,16 @@
         "--suite",
         invoker.test_suite,
       ]
+      if (use_clang_coverage) {
+        # Set a default coverage output directory (can be overridden by user
+        # passing the same flag).
+        _rebased_coverage_dir =
+            rebase_path("$root_out_dir/coverage", root_build_dir)
+        executable_args += [
+          "--coverage-dir",
+          "@WrappedPath(${_rebased_coverage_dir})",
+        ]
+      }
     } else if (_test_type == "instrumentation") {
       _test_apk = "@WrappedPath(@FileArg($_rebased_apk_build_config:deps_info:apk_path))"
       if (_incremental_install) {
diff --git a/cc/layers/layer.cc b/cc/layers/layer.cc
index c799465..063e39f 100644
--- a/cc/layers/layer.cc
+++ b/cc/layers/layer.cc
@@ -1215,11 +1215,7 @@
 
 #if DCHECK_IS_ON()
 std::string Layer::DebugName() const {
-  if (inputs_.client) {
-    if (auto debug_info = inputs_.client->TakeDebugInfo(this))
-      return debug_info->ToBaseValue()->FindKey("layer_name")->GetString();
-  }
-  return "";
+  return inputs_.client ? inputs_.client->LayerDebugName(this) : "";
 }
 #endif
 
diff --git a/cc/layers/layer_client.h b/cc/layers/layer_client.h
index fb4b6408..75aeff13 100644
--- a/cc/layers/layer_client.h
+++ b/cc/layers/layer_client.h
@@ -6,6 +6,7 @@
 #define CC_LAYERS_LAYER_CLIENT_H_
 
 #include <memory>
+#include <string>
 
 #include "cc/cc_export.h"
 
@@ -31,6 +32,8 @@
   virtual std::unique_ptr<base::trace_event::TracedValue> TakeDebugInfo(
       const Layer* layer) = 0;
 
+  virtual std::string LayerDebugName(const Layer* layer) const = 0;
+
   virtual void DidChangeScrollbarsHiddenIfOverlay(bool) = 0;
 
  protected:
diff --git a/cc/paint/image_transfer_cache_entry.cc b/cc/paint/image_transfer_cache_entry.cc
index fe86b36..5939e78 100644
--- a/cc/paint/image_transfer_cache_entry.cc
+++ b/cc/paint/image_transfer_cache_entry.cc
@@ -429,10 +429,6 @@
       if (!reader.valid())
         return false;
       DCHECK(SkIsAlign4(reinterpret_cast<uintptr_t>(plane_pixel_data)));
-      const size_t plane_size = GrContext::ComputeTextureSize(
-          yuv_plane_color_type, plane_width, plane_height, gr_mips);
-      size_ += plane_size;
-      plane_sizes_.push_back(plane_size);
 
       // Const-cast away the "volatile" on |pixel_data|. We specifically
       // understand that a malicious caller may change our pixels under us, and
@@ -449,6 +445,11 @@
       if (!plane)
         return false;
       DCHECK(plane->isTextureBacked());
+
+      const size_t plane_size = GrContext::ComputeImageSize(plane, gr_mips);
+      size_ += plane_size;
+      plane_sizes_.push_back(plane_size);
+
       // |plane_images_| must be set for use in EnsureMips(), memory dumps, and
       // unit tests.
       plane_images_.push_back(std::move(plane));
@@ -501,16 +502,19 @@
   if (width == 0 || height == 0)
     return false;
 
-  // Match GrTexture::onGpuMemorySize so that memory traces agree.
-  auto gr_mips = has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo;
-  size_ = GrContext::ComputeTextureSize(color_type, width, height, gr_mips);
-
   // Const-cast away the "volatile" on |pixel_data|. We specifically understand
   // that a malicious caller may change our pixels under us, and are OK with
   // this as the worst case scenario is visual corruption.
   SkPixmap pixmap(image_info, const_cast<const void*>(pixel_data),
                   image_info.minRowBytes());
   image_ = MakeSkImage(pixmap, width, height, target_color_space);
+
+  if (image_) {
+    // Match GrTexture::onGpuMemorySize so that memory traces agree.
+    auto gr_mips = has_mips_ ? GrMipMapped::kYes : GrMipMapped::kNo;
+    size_ = GrContext::ComputeImageSize(image_, gr_mips);
+  }
+
   return !!image_;
 }
 
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc
index dabb930..198f910 100644
--- a/cc/tiles/gpu_image_decode_cache.cc
+++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -487,8 +487,7 @@
 
 size_t GetUploadedTextureSizeFromSkImage(const sk_sp<SkImage>& plane,
                                          const GrMipMapped mipped) {
-  const size_t plane_size = GrContext::ComputeTextureSize(
-      plane->colorType(), plane->width(), plane->height(), mipped);
+  const size_t plane_size = GrContext::ComputeImageSize(plane, mipped);
   return plane_size;
 }
 
diff --git a/chrome/android/java/res/drawable/tab_indicator.xml b/chrome/android/java/res/drawable/tab_indicator.xml
new file mode 100644
index 0000000..8fac8197
--- /dev/null
+++ b/chrome/android/java/res/drawable/tab_indicator.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2019 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:left="@dimen/tab_indicator_padding"
+        android:right="@dimen/tab_indicator_padding" >
+        <shape android:shape="rectangle" >
+            <corners
+                android:topRightRadius="@dimen/tab_indicator_radius"
+                android:topLeftRadius="@dimen/tab_indicator_radius" />
+        </shape>
+    </item>
+</layer-list>
diff --git a/chrome/android/java/res/layout/clear_browsing_data_tabs.xml b/chrome/android/java/res/layout/clear_browsing_data_tabs.xml
index b64746c..89e60b0d 100644
--- a/chrome/android/java/res/layout/clear_browsing_data_tabs.xml
+++ b/chrome/android/java/res/layout/clear_browsing_data_tabs.xml
@@ -19,11 +19,7 @@
         android:layoutDirection="ltr"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        app:tabTextColor="@color/default_text_color_tertiary"
-        app:tabSelectedTextColor="@color/tab_layout_selected_tab_color"
-        app:tabMode="fixed"
-        app:tabMaxWidth="0dp"
-        app:tabGravity="fill"/>
+        style="@style/TabLayoutStyle" />
 
     <android.support.v4.view.ViewPager
         android:id="@+id/clear_browsing_data_viewpager"
diff --git a/chrome/android/java/res/layout/infobar_translate_compact_content.xml b/chrome/android/java/res/layout/infobar_translate_compact_content.xml
index 449f21d..fd9dbd9 100644
--- a/chrome/android/java/res/layout/infobar_translate_compact_content.xml
+++ b/chrome/android/java/res/layout/infobar_translate_compact_content.xml
@@ -18,7 +18,9 @@
         android:layout_weight="1"
         android:requiresFadingEdge="horizontal"
         android:fadingEdgeLength="@dimen/infobar_translate_fade_edge_length"
-        app:tabIndicatorColor="@color/tab_layout_selected_tab_color"
+        app:tabIndicator="@drawable/tab_indicator"
+        app:tabIndicatorFullWidth="false"
+        app:tabIndicatorHeight="3dp"
         app:tabSelectedTextColor="@color/tab_layout_selected_tab_color"
         app:tabGravity="fill"
         app:tabMode="scrollable" />
diff --git a/chrome/android/java/res/values/dimens.xml b/chrome/android/java/res/values/dimens.xml
index 67b96191..becf40d 100644
--- a/chrome/android/java/res/values/dimens.xml
+++ b/chrome/android/java/res/values/dimens.xml
@@ -685,4 +685,9 @@
     <dimen name="theme_preferences_checkbox_margin_end">8dp</dimen>
     <dimen name="theme_preferences_checkbox_container_padding_start">60dp</dimen>
     <dimen name="theme_preferences_checkbox_container_padding_end">16dp</dimen>
+
+    <!-- Dimens of tab indicator -->
+    <dimen name="tab_indicator_height">3dp</dimen>
+    <dimen name="tab_indicator_radius">3dp</dimen>
+    <dimen name="tab_indicator_padding">2dp</dimen>
 </resources>
diff --git a/chrome/android/java/res/values/styles.xml b/chrome/android/java/res/values/styles.xml
index e312117..d0e2ac6 100644
--- a/chrome/android/java/res/values/styles.xml
+++ b/chrome/android/java/res/values/styles.xml
@@ -633,6 +633,20 @@
         <item name="android:textSize">@dimen/text_size_medium</item>
     </style>
 
+    <!-- Tab Layout -->
+    <style name="TabLayoutStyle">
+        <item name="tabIndicator">@drawable/tab_indicator</item>
+        <item name="tabIndicatorFullWidth">false</item>
+        <item name="tabIndicatorHeight">@dimen/tab_indicator_height</item>
+        <item name="tabPaddingStart">5dp</item>
+        <item name="tabPaddingEnd">5dp</item>
+        <item name="tabMode">fixed</item>
+        <item name="tabGravity">fill</item>
+        <item name="tabTextColor">@color/default_text_color_secondary</item>
+        <item name="tabSelectedTextColor">@color/tab_layout_selected_tab_color</item>
+        <item name="android:background">@color/modern_primary_color</item>
+    </style>
+
     <!-- Misc styles -->
     <style name="LocationBarButton">
         <item name="android:background">@null</item>
diff --git a/chrome/android/java/res_download/layout/download_home_tabs.xml b/chrome/android/java/res_download/layout/download_home_tabs.xml
index a7fe631..91e0ec9 100644
--- a/chrome/android/java/res_download/layout/download_home_tabs.xml
+++ b/chrome/android/java/res_download/layout/download_home_tabs.xml
@@ -14,12 +14,7 @@
         android:id="@+id/tabs"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        app:tabMaxWidth="0dp"
-        app:tabMode="fixed"
-        app:tabGravity="fill"
-        app:tabTextColor="@color/default_text_color_tertiary"
-        app:tabSelectedTextColor="@color/tab_layout_selected_tab_color"
-        android:background="@color/modern_primary_color">
+        style="@style/TabLayoutStyle" >
 
         <android.support.design.widget.TabItem
             android:id="@+id/files_tab"
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
index 27297e3..968e232 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/bookmarks/BookmarkItemRow.java
@@ -6,7 +6,7 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 
 import org.chromium.chrome.R;
@@ -76,14 +76,8 @@
     @Override
     public void onLargeIconAvailable(Bitmap icon, int fallbackColor, boolean isFallbackColorDefault,
             @IconType int iconType) {
-        if (icon == null) {
-            mIconGenerator.setBackgroundColor(fallbackColor);
-            icon = mIconGenerator.generateIconForUrl(mUrl);
-            setIconDrawable(new BitmapDrawable(getResources(), icon));
-        } else {
-            setIconDrawable(FaviconUtils.createRoundedBitmapDrawable(getResources(),
-                    Bitmap.createScaledBitmap(
-                            icon, mDisplayedIconSize, mDisplayedIconSize, false)));
-        }
+        Drawable iconDrawable = FaviconUtils.getIconDrawableWithoutFilter(
+                icon, mUrl, fallbackColor, mIconGenerator, getResources(), mDisplayedIconSize);
+        setIconDrawable(iconDrawable);
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
index 9b4bc6c..3679840 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/bottombar/ephemeraltab/EphemeralTabCoordinator.java
@@ -6,7 +6,6 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 
@@ -232,14 +231,8 @@
          */
         private Drawable faviconDrawable(Bitmap image, String url) {
             if (url == null) return null;
-            if (image == null) {
-                image = mIconGenerator.generateIconForUrl(url);
-                return new BitmapDrawable(mContext.getResources(),
-                        Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, true));
-            }
-
-            return FaviconUtils.createRoundedBitmapDrawable(mContext.getResources(),
-                    Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, true));
+            return FaviconUtils.getIconDrawableWithFilter(
+                    image, url, mIconGenerator, mContext.getResources(), mFaviconSize);
         }
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java b/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
index 0cd0833..df60dc1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/items/OfflineContentAggregatorNotificationBridgeUi.java
@@ -10,7 +10,6 @@
 import org.chromium.chrome.browser.download.DownloadServiceDelegate;
 import org.chromium.components.offline_items_collection.ContentId;
 import org.chromium.components.offline_items_collection.LaunchLocation;
-import org.chromium.components.offline_items_collection.LegacyHelpers;
 import org.chromium.components.offline_items_collection.OfflineContentProvider;
 import org.chromium.components.offline_items_collection.OfflineItem;
 import org.chromium.components.offline_items_collection.OfflineItemState;
@@ -150,7 +149,7 @@
     private void pushItemToUi(OfflineItem item, OfflineItemVisuals visuals) {
         // TODO(http://crbug.com/855141): Find a cleaner way to hide unimportant UI updates.
         // If it's a suggested page, do not add it to the notification UI.
-        if (LegacyHelpers.isLegacyOfflinePage(item.id) && item.isSuggested) return;
+        if (item.isSuggested) return;
 
         DownloadInfo info = DownloadInfo.fromOfflineItem(item, visuals);
         switch (item.state) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/favicon/FaviconUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/favicon/FaviconUtils.java
index 35f93d8..a9b9194 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/favicon/FaviconUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/favicon/FaviconUtils.java
@@ -6,8 +6,12 @@
 
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
 
+import androidx.annotation.Nullable;
+
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ui.widget.RoundedIconGenerator;
@@ -59,6 +63,56 @@
                 resources.getDimensionPixelSize(R.dimen.default_favicon_corner_radius));
     }
 
+    /**
+     * Creates a {@link Drawable} with the provided icon with
+     * nearest-neighbor scaling through {@link Bitmap#createScaledBitmap(Bitmap, int, int,
+     * boolean)}, or a fallback monogram.
+     * @param icon {@link Bitmap} with the icon to display. If null, a fallback monogram will be
+     *         generated.
+     * @param url Url to generate a monogram. Used only if {@code icon} is null.
+     * @param fallbackColor Color to generate a monogram. Used only if {@code icon} is null.
+     * @param iconGenerator RoundedIconGenerator to generate a monogram. Used only if {@code icon}
+     *         is null. Side effect: {@link RoundedIconGenerator#setBackgroundColor(int)} will be
+     *         called.
+     * @param resources {@link Resources} to create a {@link BitmapDrawable}.
+     * @param iconSize Width and height of the returned icon in px.
+     * @return A {@link Drawable} to be displayed as the favicon.
+     */
+    public static Drawable getIconDrawableWithoutFilter(@Nullable Bitmap icon, String url,
+            int fallbackColor, RoundedIconGenerator iconGenerator, Resources resources,
+            int iconSize) {
+        if (icon == null) {
+            iconGenerator.setBackgroundColor(fallbackColor);
+            icon = iconGenerator.generateIconForUrl(url);
+            return new BitmapDrawable(resources, icon);
+        }
+        return createRoundedBitmapDrawable(
+                resources, Bitmap.createScaledBitmap(icon, iconSize, iconSize, false));
+    }
+
+    /**
+     * Creates a {@link Drawable} with the provided icon, or a fallback monogram, with bilinear
+     * scaling through {@link Bitmap#createScaledBitmap(Bitmap, int, int, boolean)}.
+     * @param icon {@link Bitmap} with the icon to display. If null, a fallback monogram will be
+     *         generated.
+     * @param url Url to generate a monogram. Used only if {@code icon} is null.
+     * @param iconGenerator RoundedIconGenerator to generate a monogram. Used only if {@code icon}
+     *         is null.
+     * @param resources {@link Resources} to create a {@link BitmapDrawable}.
+     * @param iconSize Width and height of the returned icon.
+     * @return A {@link Drawable} to be displayed as the favicon.
+     */
+    public static Drawable getIconDrawableWithFilter(@Nullable Bitmap icon, String url,
+            RoundedIconGenerator iconGenerator, Resources resources, int iconSize) {
+        if (icon == null) {
+            icon = iconGenerator.generateIconForUrl(url);
+            return new BitmapDrawable(
+                    resources, Bitmap.createScaledBitmap(icon, iconSize, iconSize, true));
+        }
+        return createRoundedBitmapDrawable(
+                resources, Bitmap.createScaledBitmap(icon, iconSize, iconSize, true));
+    }
+
     private static int getIconColor(Resources resources) {
         return ApiCompatibilityUtils.getColor(resources, R.color.default_favicon_background_color);
     }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
index 2d75eac8..4bdefd1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/history/HistoryItemView.java
@@ -6,7 +6,7 @@
 
 import android.content.Context;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.support.graphics.drawable.VectorDrawableCompat;
 import android.support.v4.view.ViewCompat;
 import android.support.v7.content.res.AppCompatResources;
@@ -163,16 +163,9 @@
     @Override
     public void onLargeIconAvailable(Bitmap icon, int fallbackColor, boolean isFallbackColorDefault,
             @IconType int iconType) {
-        // TODO(twellington): move this somewhere that can be shared with bookmarks.
-        if (icon == null) {
-            mIconGenerator.setBackgroundColor(fallbackColor);
-            icon = mIconGenerator.generateIconForUrl(getItem().getUrl());
-            setIconDrawable(new BitmapDrawable(getResources(), icon));
-        } else {
-            setIconDrawable(FaviconUtils.createRoundedBitmapDrawable(getResources(),
-                    Bitmap.createScaledBitmap(
-                            icon, mDisplayedIconSize, mDisplayedIconSize, false)));
-        }
+        Drawable drawable = FaviconUtils.getIconDrawableWithoutFilter(icon, getItem().getUrl(),
+                fallbackColor, mIconGenerator, getResources(), mDisplayedIconSize);
+        setIconDrawable(drawable);
     }
 
     private void requestIcon() {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java
index 91fa8d75..3adaf4084 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsRowAdapter.java
@@ -9,7 +9,6 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.PorterDuff;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.LruCache;
@@ -505,9 +504,11 @@
                 viewHolder.textView.setText(R.string.show_full_history);
                 Bitmap historyIcon = BitmapFactory.decodeResource(
                         mActivity.getResources(), R.drawable.ic_watch_later_24dp);
-                Drawable drawable = getRoundedFavicon(historyIcon,
-                        mActivity.getResources().getDimensionPixelSize(
-                                R.dimen.tile_view_icon_size_modern));
+                int size = mActivity.getResources().getDimensionPixelSize(
+                        R.dimen.tile_view_icon_size_modern);
+                Drawable drawable =
+                        FaviconUtils.createRoundedBitmapDrawable(mActivity.getResources(),
+                                Bitmap.createScaledBitmap(historyIcon, size, size, true));
                 drawable.setColorFilter(ApiCompatibilityUtils.getColor(mActivity.getResources(),
                                                 R.color.default_icon_color),
                         PorterDuff.Mode.SRC_IN);
@@ -703,19 +704,8 @@
 
     private Drawable faviconDrawable(Bitmap image, String url) {
         if (url == null) return null;
-        if (image == null) {
-            image = mIconGenerator.generateIconForUrl(url);
-            return new BitmapDrawable(mActivity.getResources(),
-                    Bitmap.createScaledBitmap(image, mFaviconSize, mFaviconSize, true));
-        }
-        return getRoundedFavicon(image, mFaviconSize);
-    }
-
-    private Drawable getRoundedFavicon(Bitmap image, int size) {
-        // TODO(injae): Move shared code between Bookmarks/History/Downloads/here to ViewUtils.java.
-        // Also applies to RoundedIconGenerator. crbug.com/829550
-        return FaviconUtils.createRoundedBitmapDrawable(
-                mActivity.getResources(), Bitmap.createScaledBitmap(image, size, size, true));
+        return FaviconUtils.getIconDrawableWithFilter(
+                image, url, mIconGenerator, mActivity.getResources(), mFaviconSize);
     }
 
     private void loadForeignFavicon(final ViewHolder viewHolder, final String url) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java
index c418a42d..27b61ad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java
@@ -12,7 +12,6 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.support.v4.app.DialogFragment;
@@ -117,23 +116,13 @@
                 public void onLargeIconAvailable(Bitmap icon, int fallbackColor,
                         boolean isFallbackColorDefault, @IconType int iconType) {
                     if (this != viewHolder.imageCallback) return;
-                    Drawable image = getFaviconDrawable(icon, fallbackColor, url);
+                    Drawable image = FaviconUtils.getIconDrawableWithoutFilter(
+                            icon, url, fallbackColor, mIconGenerator, getResources(), mFaviconSize);
                     viewHolder.imageView.setImageDrawable(image);
                 }
             };
             mLargeIconBridge.getLargeIconForUrl(url, mFaviconSize, viewHolder.imageCallback);
         }
-
-        private Drawable getFaviconDrawable(Bitmap icon, int fallbackColor, String url) {
-            if (icon == null) {
-                mIconGenerator.setBackgroundColor(fallbackColor);
-                icon = mIconGenerator.generateIconForUrl(url);
-                return new BitmapDrawable(getResources(), icon);
-            } else {
-                return FaviconUtils.createRoundedBitmapDrawable(getResources(),
-                        Bitmap.createScaledBitmap(icon, mFaviconSize, mFaviconSize, false));
-            }
-        }
     }
 
     /**
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
index f382f5a..d4889507 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/offlinepages/OfflinePageUtilsTest.java
@@ -289,8 +289,13 @@
 
     @Test
     @MediumTest
-    @CommandLineFlags.Add({"enable-features=OfflinePagesSharing"})
-    public void testShareTemporaryOfflinePage() throws Exception {
+    @CommandLineFlags
+            .Add({"enable-features=OfflinePagesSharing"})
+            @DisableIf.Build(message = "https://crbug.com/1001506",
+                    sdk_is_greater_than = Build.VERSION_CODES.N,
+                    sdk_is_less_than = Build.VERSION_CODES.P)
+            public void
+            testShareTemporaryOfflinePage() throws Exception {
         loadOfflinePage(SUGGESTED_ARTICLES_ID);
         final Semaphore semaphore = new Semaphore(0);
         final TestShareCallback shareCallback = new TestShareCallback(semaphore);
diff --git a/chrome/android/trichrome.gni b/chrome/android/trichrome.gni
index 0ac676b..3f252e4 100644
--- a/chrome/android/trichrome.gni
+++ b/chrome/android/trichrome.gni
@@ -152,12 +152,15 @@
         "//chrome/android/java/trichrome.flags",
       ]
       if (trichrome_synchronized_proguard) {
-        proguard_configs += [ "//chrome/android/java/static_library_dex_reference_workarounds.flags" ]
-      }
-      if (enable_proguard_obfuscation) {
-        proguard_configs +=
-            [ "//base/android/proguard/enable_obfuscation.flags" ]
+        proguard_configs += [
+          "//chrome/android/java/static_library_dex_reference_workarounds.flags",
+          "//base/android/proguard/enable_obfuscation.flags",
+        ]
       } else {
+        # Disabling all obfuscation for the Trichrome library as a temporary
+        # workaround for crbug.com/1012842. There were naming conflicts between
+        # Library and Chrome, since each Proguard run doesn't know about the
+        # other, and thus handed out the first names (a, b, c) to both.
         proguard_configs +=
             [ "//base/android/proguard/disable_all_obfuscation.flags" ]
       }
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 891661a..e7a238b 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -4570,9 +4570,12 @@
       <message name="IDS_PASSWORD_MANAGER_ACCOUNT_CHOOSER_TITLE" desc="The title of the account chooser dialog.">
         Sign in as
       </message>
-      <message name="IDS_PASSWORD_MANAGER_CONFIRM_SAVED_TITLE" desc="The title text used in the passwords bubble when the user has saved a password.">
+      <message name="IDS_PASSWORD_MANAGER_CONFIRM_SAVED_TITLE" desc="The title text used in the passwords bubble after the user has saved a password.">
         Password saved
       </message>
+      <message name="IDS_PASSWORD_MANAGER_SYNC_PROMO_TITLE" desc="The title text used in the sync promo bubble when the user has saved a password.">
+        Password saved on this device
+      </message>
       <message name="IDS_PASSWORD_GENERATION_SUGGESTION" desc="Text shown next to a generated password describing it as a suggestion.">
         Use suggested password
       </message>
@@ -4620,7 +4623,7 @@
         </message>
       </if>
       <message name="IDS_PASSWORD_MANAGER_SIGNIN_PROMO_LABEL" desc="Promotion text for Chrome sign-in appearing in the password bubble after a password is saved.">
-        To get your passwords on all your devices, sign in to Chrome.
+        To save passwords to your Google Account, turn on sync.
       </message>
       <!-- Password manager onboarding strings -->
       <message name="IDS_PASSWORD_MANAGER_ONBOARDING_TITLE_A" desc="The title shown in the password manager onboarding dialog.">
@@ -4641,10 +4644,10 @@
 
       <!-- Begin of Sync Promo strings for Desktop Identity Consistency. -->
       <message name="IDS_PASSWORD_MANAGER_DICE_PROMO_SIGNIN_MESSAGE" desc="Text of the sync promo displayed at the bottom of the save password bubble asking the user to sign in and enable sync.">
-        To get all your passwords on all your devices, sign in and turn on sync.
+        To save passwords to your Google Account, sign in and turn on sync.
       </message>
       <message name="IDS_PASSWORD_MANAGER_DICE_PROMO_SYNC_MESSAGE" desc="Text of the sync promo displayed at the bottom of the save password bubble asking the user to enable sync.">
-        To get all your passwords on all your devices, turn on sync.
+        To save passwords to your Google Account, turn on sync.
       </message>
       <!-- End of Sync Promo strings for Desktop Identity Consistency. -->
 
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index d54a735..101401f 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -3908,10 +3908,9 @@
      FEATURE_VALUE_TYPE(media::kHardwareMediaKeyHandling)},
 #endif
 
-    {"enable-avoid-flash-between-navigation",
-     flag_descriptions::kAvoidFlashBetweenNavigationName,
-     flag_descriptions::kAvoidFlahsBetweenNavigationDescription, kOsAll,
-     FEATURE_VALUE_TYPE(blink::features::kAvoidFlashBetweenNavigation)},
+    {"enable-paint-holding", flag_descriptions::kPaintHoldingName,
+     flag_descriptions::kPaintHoldingDescription, kOsAll,
+     FEATURE_VALUE_TYPE(blink::features::kPaintHolding)},
 
 #if !defined(OS_ANDROID)
     {"app-management", flag_descriptions::kAppManagementName,
@@ -4649,6 +4648,12 @@
      FEATURE_VALUE_TYPE(chromeos::features::kExoPointerLock)},
 #endif  // defined(OS_CHROMEOS)
 
+#if defined(OS_MACOSX)
+    {"metal", flag_descriptions::kMetalName,
+     flag_descriptions::kMetalDescription, kOsMac,
+     FEATURE_VALUE_TYPE(features::kMetal)},
+#endif
+
     // NOTE: Adding a new flag requires adding a corresponding entry to enum
     // "LoginCustomFlags" in tools/metrics/histograms/enums.xml. See "Flag
     // Histograms" in tools/metrics/histograms/README.md (run the
diff --git a/chrome/browser/android/download/download_manager_service.cc b/chrome/browser/android/download/download_manager_service.cc
index 0939cc06..b2eaa19 100644
--- a/chrome/browser/android/download/download_manager_service.cc
+++ b/chrome/browser/android/download/download_manager_service.cc
@@ -553,7 +553,8 @@
       item->GetURL(), traffic_annotation);
 
   // Retry allows redirect.
-  download_url_params->set_follow_cross_origin_redirects(true);
+  download_url_params->set_cross_origin_redirects(
+      network::mojom::RedirectMode::kFollow);
 
   // Retry is triggered through user gesture, and don't have renderer
   // associated, content initiated has to be false to avoid download being
diff --git a/chrome/browser/autofill/captured_sites_test_utils.cc b/chrome/browser/autofill/captured_sites_test_utils.cc
index 15bedda..73f3f7d 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.cc
+++ b/chrome/browser/autofill/captured_sites_test_utils.cc
@@ -174,8 +174,6 @@
   return sites;
 }
 
-constexpr base::TimeDelta PageActivityObserver::kPaintEventCheckInterval;
-
 std::string FilePathToUTF8(const base::FilePath::StringType& str) {
 #if defined(OS_WIN)
   return base::WideToUTF8(str);
@@ -184,64 +182,6 @@
 #endif
 }
 
-// PageActivityObserver -------------------------------------------------------
-PageActivityObserver::PageActivityObserver(content::WebContents* web_contents)
-    : content::WebContentsObserver(web_contents) {}
-
-PageActivityObserver::PageActivityObserver(content::RenderFrameHost* frame)
-    : content::WebContentsObserver(
-          content::WebContents::FromRenderFrameHost(frame)) {}
-
-void PageActivityObserver::WaitTillPageIsIdle(
-    base::TimeDelta continuous_paint_timeout) {
-  base::TimeTicks finished_load_time = base::TimeTicks::Now();
-  while (true) {
-    content::RenderFrameSubmissionObserver frame_submission_observer(
-        web_contents());
-    // Runs a loop for kPaintEventCheckInterval to see if the renderer is
-    // idle.
-    {
-      base::RunLoop heart_beat;
-      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-          FROM_HERE, heart_beat.QuitClosure(), kPaintEventCheckInterval);
-      heart_beat.Run();
-    }
-    bool page_is_loading =
-        web_contents()->IsWaitingForResponse() || web_contents()->IsLoading();
-    if (page_is_loading) {
-      finished_load_time = base::TimeTicks::Now();
-    } else if ((base::TimeTicks::Now() - finished_load_time) >
-               continuous_paint_timeout) {
-      // |continuous_paint_timeout| has expired since Chrome loaded the page.
-      // During this period of time, Chrome has been continuously painting
-      // the page. In this case, the page is probably idle, but a bug, a
-      // blinking caret or a persistent animation is making Chrome paint at
-      // regular intervals. Exit.
-      break;
-    } else if (frame_submission_observer.render_frame_count() == 0) {
-      // If the renderer has stopped submitting frames for the waiting interval
-      // then we're done.
-      break;
-    }
-  }
-}
-
-bool PageActivityObserver::WaitForVisualUpdate(base::TimeDelta timeout) {
-  base::TimeTicks start_time = base::TimeTicks::Now();
-  content::RenderFrameSubmissionObserver frame_submission_observer(
-      web_contents());
-  while (frame_submission_observer.render_frame_count() == 0) {
-    base::RunLoop heart_beat;
-    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
-        FROM_HERE, heart_beat.QuitClosure(), kPaintEventCheckInterval);
-    heart_beat.Run();
-    if ((base::TimeTicks::Now() - start_time) > timeout) {
-      return false;
-    }
-  }
-  return true;
-}
-
 // FrameObserver --------------------------------------------------------------
 IFrameWaiter::IFrameWaiter(content::WebContents* web_contents)
     : content::WebContentsObserver(web_contents),
@@ -429,6 +369,78 @@
   return browser_->tab_strip_model()->GetActiveWebContents();
 }
 
+void TestRecipeReplayer::WaitTillPageIsIdle(
+    base::TimeDelta continuous_paint_timeout) {
+  // Loop continually while WebContents are waiting for response or loading.
+  // page_is_loading is expectedWaitTillPageIsIdle to always got to False
+  // eventually, but adding a timeout as a fallback.
+  base::TimeTicks finished_load_time = base::TimeTicks::Now();
+  while (true) {
+    {
+      base::RunLoop heart_beat;
+      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE, heart_beat.QuitClosure(), wait_for_idle_loop_length);
+      heart_beat.Run();
+    }
+    bool page_is_loading = GetWebContents()->IsWaitingForResponse() ||
+                           GetWebContents()->IsLoading();
+    if (!page_is_loading)
+      break;
+    if ((base::TimeTicks::Now() - finished_load_time) >
+        continuous_paint_timeout) {
+      VLOG(1) << "Page is still loading after "
+              << visual_update_timeout.InSeconds()
+              << " seconds. Bailing because timeout was reached.";
+      break;
+    }
+  }
+  finished_load_time = base::TimeTicks::Now();
+  while (true) {
+    // Now, rely on the render frame count to be the indicator of page activity.
+    // Once all the frames are drawn, we're free to continue.
+    content::RenderFrameSubmissionObserver frame_submission_observer(
+        GetWebContents());
+    {
+      base::RunLoop heart_beat;
+      base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+          FROM_HERE, heart_beat.QuitClosure(), wait_for_idle_loop_length);
+      heart_beat.Run();
+    }
+    if (frame_submission_observer.render_frame_count() == 0) {
+      // If the render r has stopped submitting frames
+      break;
+    } else if ((base::TimeTicks::Now() - finished_load_time) >
+               continuous_paint_timeout) {
+      // |continuous_paint_timeout| has expired since Chrome loaded the page.
+      // During this period of time, Chrome has been continuously painting
+      // the page. In this case, the page is probably idle, but a bug, a
+      // blinking caret or a persistent animation is keeping the
+      // |render_frame_count| from reaching zero. Exit.
+      VLOG(1) << "Wait for render frame count timed out after "
+              << continuous_paint_timeout.InSeconds()
+              << " seconds with the frame count still at: "
+              << frame_submission_observer.render_frame_count();
+      break;
+    }
+  }
+}
+
+bool TestRecipeReplayer::WaitForVisualUpdate(base::TimeDelta timeout) {
+  base::TimeTicks start_time = base::TimeTicks::Now();
+  content::RenderFrameSubmissionObserver frame_submission_observer(
+      GetWebContents());
+  while (frame_submission_observer.render_frame_count() == 0) {
+    base::RunLoop heart_beat;
+    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+        FROM_HERE, heart_beat.QuitClosure(), wait_for_idle_loop_length);
+    heart_beat.Run();
+    if ((base::TimeTicks::Now() - start_time) > timeout) {
+      return false;
+    }
+  }
+  return true;
+}
+
 void TestRecipeReplayer::CleanupSiteData() {
   // Navigate to about:blank, then clear the browser cache.
   // Navigating to about:blank before clearing the cache ensures that
@@ -764,7 +776,6 @@
   }
 
   // Navigate to the starting URL, wait for the page to complete loading.
-  PageActivityObserver page_activity_observer(GetWebContents());
   if (!content::ExecuteScript(GetWebContents(),
                               base::StringPrintf("window.location.href = '%s';",
                                                  starting_url->c_str()))) {
@@ -772,7 +783,7 @@
     return false;
   }
 
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
@@ -782,13 +793,11 @@
   content::RenderFrameHost* frame;
   if (!ExtractFrameAndVerifyElement(action, &xpath, &frame))
     return false;
-
   std::vector<std::string> frame_path;
   if (!GetIFramePathFromAction(action, &frame_path))
     return false;
 
   VLOG(1) << "Invoking Chrome Autofill on `" << xpath << "`.";
-  PageActivityObserver page_activity_observer(frame);
   // Clear the input box first, in case a previous value is there.
   // If the text input box is not clear, pressing the down key will not
   // bring up the autofill suggestion box.
@@ -805,8 +814,7 @@
   if (!feature_action_executor()->AutofillForm(
           xpath, frame_path, kAutofillActionNumRetries, frame))
     return false;
-  page_activity_observer.WaitTillPageIsIdle(
-      kAutofillActionWaitForVisualUpdateTimeout);
+  WaitTillPageIsIdle(kAutofillActionWaitForVisualUpdateTimeout);
   return true;
 }
 
@@ -818,9 +826,9 @@
     return false;
 
   VLOG(1) << "Left mouse clicking `" << xpath << "`.";
-  PageActivityObserver page_activity_observer(frame);
   if (!ScrollElementIntoView(xpath, frame))
     return false;
+  WaitTillPageIsIdle(scroll_wait_timeout);
 
   gfx::Rect rect;
   if (!GetBoundingRectOfTargetElement(xpath, frame, &rect))
@@ -828,7 +836,7 @@
   if (!SimulateLeftMouseClickAt(rect.CenterPoint(), frame))
     return false;
 
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
@@ -869,10 +877,10 @@
     return false;
 
   VLOG(1) << "Hovering over `" << xpath << "`.";
-  PageActivityObserver page_activity_observer(frame);
 
   if (!ScrollElementIntoView(xpath, frame))
     return false;
+  WaitTillPageIsIdle(scroll_wait_timeout);
 
   gfx::Rect rect;
   if (!GetBoundingRectOfTargetElement(xpath, frame, &rect))
@@ -881,7 +889,7 @@
   if (!SimulateMouseHoverAt(frame, rect.CenterPoint()))
     return false;
 
-  if (!page_activity_observer.WaitForVisualUpdate()) {
+  if (!WaitForVisualUpdate()) {
     ADD_FAILURE() << "The page did not respond to a mouse hover action!";
     return false;
   }
@@ -901,8 +909,7 @@
   VLOG(1) << "Making explicit URL redirect to '" << *url << "'";
   ui_test_utils::NavigateToURL(browser_, GURL(*url));
 
-  PageActivityObserver page_activity_observer(GetWebContents());
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
 
   return true;
 }
@@ -915,19 +922,17 @@
     return false;
 
   VLOG(1) << "Pressing 'Enter' on `" << xpath << "`.";
-  PageActivityObserver page_activity_observer(frame);
   SimulateKeyPressWrapper(content::WebContents::FromRenderFrameHost(frame),
                           ui::DomKey::ENTER);
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
 bool TestRecipeReplayer::ExecutePressEscapeAction(
     const base::DictionaryValue& action) {
   VLOG(1) << "Pressing 'Esc' in the current frame";
-  PageActivityObserver page_activity_observer(GetWebContents());
   SimulateKeyPressWrapper(GetWebContents(), ui::DomKey::ESCAPE);
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
@@ -938,11 +943,10 @@
   if (!ExtractFrameAndVerifyElement(action, &xpath, &frame, true))
     return false;
 
-  PageActivityObserver page_activity_observer(frame);
   VLOG(1) << "Pressing 'Space' on `" << xpath << "`.";
   SimulateKeyPressWrapper(content::WebContents::FromRenderFrameHost(frame),
                           ui::DomKey::FromCharacter(' '));
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
@@ -972,7 +976,6 @@
   VLOG(1) << "Running JavaScript commands on the page.";
 
   // Execute the commands.
-  PageActivityObserver page_activity_observer(frame);
   for (const std::string& command : commands) {
     if (!content::ExecuteScript(frame, command)) {
       ADD_FAILURE() << "Failed to execute JavaScript command `" << command
@@ -981,7 +984,7 @@
     }
     // Wait in case the JavaScript command triggers page load or layout
     // changes.
-    page_activity_observer.WaitTillPageIsIdle();
+    WaitTillPageIsIdle();
   }
 
   return true;
@@ -1020,7 +1023,6 @@
     return false;
 
   VLOG(1) << "Select option '" << index.value() << "' from `" << xpath << "`.";
-  PageActivityObserver page_activity_observer(frame);
   if (!ExecuteJavaScriptOnElementByXpath(
           frame, xpath,
           base::StringPrintf(
@@ -1030,8 +1032,7 @@
     ADD_FAILURE() << "Failed to select drop down option with JavaScript!";
     return false;
   }
-
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
@@ -1048,7 +1049,6 @@
     return false;
 
   VLOG(1) << "Typing '" << *value << "' inside `" << xpath << "`.";
-  PageActivityObserver page_activity_observer(frame);
   if (!ExecuteJavaScriptOnElementByXpath(
           frame, xpath,
           base::StringPrintf(
@@ -1057,8 +1057,7 @@
     ADD_FAILURE() << "Failed to type inside input element with JavaScript!";
     return false;
   }
-
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
@@ -1085,12 +1084,11 @@
   VLOG(1) << "Typing '" << *value << "' inside `" << xpath << "`.";
   content::WebContents* web_contents =
       content::WebContents::FromRenderFrameHost(frame);
-  PageActivityObserver page_activity_observer(web_contents);
   for (size_t index = 0; index < value->size(); index++) {
     SimulateKeyPressWrapper(web_contents,
                             ui::DomKey::FromCharacter(value->at(index)));
   }
-  page_activity_observer.WaitTillPageIsIdle();
+  WaitTillPageIsIdle();
   return true;
 }
 
@@ -1318,7 +1316,6 @@
   if (!GetTargetHTMLElementVisibilityEnumFromAction(action,
                                                     &visibility_enum_val))
     return false;
-
   if (!GetTargetFrameFromAction(action, frame))
     return false;
 
@@ -1428,14 +1425,12 @@
     const std::vector<std::string>& state_assertions,
     const base::TimeDelta& timeout) {
   base::TimeTicks start_time = base::TimeTicks::Now();
-  PageActivityObserver page_activity_observer(
-      content::WebContents::FromRenderFrameHost(frame));
   while (!AllAssertionsPassed(frame, state_assertions)) {
     if (base::TimeTicks::Now() - start_time > timeout) {
         ADD_FAILURE() << "State change hasn't completed within timeout.";
         return false;
     }
-    page_activity_observer.WaitTillPageIsIdle();
+    WaitTillPageIsIdle();
   }
   return true;
 }
@@ -1547,7 +1542,6 @@
     ADD_FAILURE() << "Failed to scroll the element into view with JavaScript!";
     return false;
   }
-
   return true;
 }
 
diff --git a/chrome/browser/autofill/captured_sites_test_utils.h b/chrome/browser/autofill/captured_sites_test_utils.h
index 851cce1..9d01282 100644
--- a/chrome/browser/autofill/captured_sites_test_utils.h
+++ b/chrome/browser/autofill/captured_sites_test_utils.h
@@ -30,18 +30,24 @@
 // 1. A page load error occurred and the page does not have a page element
 //    the test expects. Test should stop waiting.
 // 2. A page contains persistent animation (such as a flash sale count down
-//    timer) that causes the framework's paint-based PageActivityObserver to
-//    wait indefinitely. Test should stop waiting if a sufficiently large time
-//    has expired for the page to load or for the page to respond to the last
-//    user action.
+//    timer) that causes the RenderFrameHost count to never diminish completely.
+//    Test should stop waiting if a sufficiently large time has expired for the
+//    page to load or for the page to respond to the last user action.
 const base::TimeDelta default_action_timeout = base::TimeDelta::FromSeconds(30);
 // The amount of time to wait for a page to trigger a paint in response to a
 // an action. The Captured Site Automation Framework uses this timeout to
 // break out of a wait loop after a hover action.
 const base::TimeDelta visual_update_timeout = base::TimeDelta::FromSeconds(20);
+// When we cause a scroll event, make sure we give the page a moment to react
+// before continuing.
+const base::TimeDelta scroll_wait_timeout = base::TimeDelta::FromSeconds(2);
 // Some times, tests tend to need a break that can't be read from the elements
-// play status
+// play status.
 const base::TimeDelta cool_off_action_timeout = base::TimeDelta::FromSeconds(1);
+// The time to wait between checks for a page to become idle or active based on
+// the loading status and then the render frame count.
+const base::TimeDelta wait_for_idle_loop_length =
+    base::TimeDelta::FromMilliseconds(500);
 
 std::string FilePathToUTF8(const base::FilePath::StringType& str);
 
@@ -73,58 +79,6 @@
   }
 };
 
-// PageActivityObserver
-//
-// PageActivityObserver is a universal wait-for-page-ready object that ensures
-// the current web page finishes responding to user input. The Captured Site
-// Automation Framework, specifically the TestRecipeReplayer class, uses
-// PageActivityObserver to time delays between two test actions. Without the
-// delay, a test may break itself by performing a page action before the page
-// is ready to receive the action.
-//
-// For example, the Amazon.com checkout page runs background scripts after
-// loading. While running the background scripts, the checkout page displays
-// a spinner. If a user clicks on a link while the spinner is present,
-// Amazon.com will dispatch the user to an error page.
-//
-// Page readiness is hard to determine because on real-world sites, page
-// readiness does not correspond to page load events. In the above Amazon.com
-// example, the checkout page starts the background scripts after the page
-// finishes loading.
-//
-// The PageActivityObserver defines page ready as the absence of Chrome paint
-// events. On real-world sites, if a page is busy loading, the Chrome tab
-// should be busy and Chrome should continuously make layout changes and
-// repaint the page. If a site is busy doing background work, most pages
-// typically display some form of persistent animation such as a progress bar
-// or a spinner to tell the user that the page is not ready. Therefore, it
-// is reasonable to assume that a page is ready if Chrome finished painting.
-class PageActivityObserver : public content::WebContentsObserver {
- public:
-  explicit PageActivityObserver(content::WebContents* web_contents);
-  explicit PageActivityObserver(content::RenderFrameHost* frame);
-  ~PageActivityObserver() override = default;
-
-  // Wait until Chrome finishes loading a page and updating the page's visuals.
-  // If Chrome finishes loading a page but continues to paint every half
-  // second, exit after |continuous_paint_timeout| expires since Chrome
-  // finished loading the page.
-  void WaitTillPageIsIdle(
-      base::TimeDelta continuous_paint_timeout = default_action_timeout);
-  // Wait until Chrome makes at least 1 visual update, or until timeout
-  // expires.
-  bool WaitForVisualUpdate(base::TimeDelta timeout = visual_update_timeout);
-
- private:
-  // PageActivityObserver determines if Chrome stopped painting by checking if
-  // the renderer hasn't submitted a compositor frame for a specific amount of
-  // time. kPaintEventCheckInterval defines this amount of time.
-  static constexpr base::TimeDelta kPaintEventCheckInterval =
-      base::TimeDelta::FromMilliseconds(500);
-
-  DISALLOW_COPY_AND_ASSIGN(PageActivityObserver);
-};
-
 // IFrameWaiter
 //
 // IFrameWaiter is an waiter object that waits for an iframe befitting a
@@ -242,7 +196,6 @@
   ~TestRecipeReplayer();
   void Setup();
   void Cleanup();
-
   // Replay a test by:
   // 1. Starting a WPR server using the specified capture file.
   // 2. Replaying the specified Test Recipe file.
@@ -362,7 +315,16 @@
   bool SetupSavedAutofillProfile(
       const base::Value& saved_autofill_profile_container);
   bool SetupSavedPasswords(const base::Value& saved_password_list_container);
-
+  // Wait until Chrome finishes loading a page and updating the page's visuals.
+  // If Chrome finishes loading a page but continues to paint every half
+  // second, exit after |continuous_paint_timeout| expires since Chrome
+  // finished loading the page.
+  void WaitTillPageIsIdle(
+      base::TimeDelta continuous_paint_timeout = default_action_timeout);
+  // Wait until Chrome makes at least 1 visual update, or until timeout
+  // expires. Returns false if no visual update is observed before the given
+  // timeout elapses.
+  bool WaitForVisualUpdate(base::TimeDelta timeout = visual_update_timeout);
   Browser* browser_;
   TestRecipeReplayChromeFeatureActionExecutor* feature_action_executor_;
 
diff --git a/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog_unittest.cc b/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog_unittest.cc
index ea95c7c..41d11d4 100644
--- a/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog_unittest.cc
+++ b/chrome/browser/chromeos/arc/intent_helper/arc_external_protocol_dialog_unittest.cc
@@ -84,7 +84,7 @@
 // TODO(crbug.com/1011364): Extract this into a common mock file.
 class MockSharingService : public SharingService {
  public:
-  explicit MockSharingService()
+  MockSharingService()
       : SharingService(
             /*sync_prefs=*/nullptr,
             /*vapid_key_manager=*/nullptr,
@@ -1021,6 +1021,7 @@
   devices.emplace_back(std::make_unique<syncer::DeviceInfo>(
       device_guid, "device_name", "chrome_version", "user_agent",
       sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/base::Time::Now(),
       /*send_tab_to_self_receiving_enabled=*/false,
       /*sharing_info=*/base::nullopt));
diff --git a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
index 9575930..5e73bcd 100644
--- a/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
+++ b/chrome/browser/chromeos/child_accounts/screen_time_controller_browsertest.cc
@@ -153,7 +153,7 @@
 };
 
 // Tests a simple lock override.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_LockOverride) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, LockOverride) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -182,7 +182,7 @@
 }
 
 // Tests an unlock override on a bedtime.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_UnlockBedtime) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockBedtime) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 22:00:00 BRT");
   ScreenLockerTester().Lock();
 
@@ -234,8 +234,7 @@
 }
 
 // Tests an override with duration on a bedtime before it's locked.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_OverrideBedtimeWithDuration) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, OverrideBedtimeWithDuration) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 20:45:00 PST");
   ScreenLockerTester().Lock();
 
@@ -305,7 +304,7 @@
 
 // Tests an override with duration on a daily limit before it's locked.
 IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_OverrideDailyLimitWithDuration) {
+                       OverrideDailyLimitWithDuration) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 BRT");
   ScreenLockerTester().Lock();
 
@@ -369,8 +368,7 @@
 }
 
 // Tests an unlock override with duration on a bedtime.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_UnlockBedtimeWithDuration) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockBedtimeWithDuration) {
   LogInChildAndSetupClockWithTime("5 Jan 2018 22:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -436,8 +434,7 @@
 }
 
 // Tests an unlock override with duration on a daily limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_UnlockDailyLimitWithDuration) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, UnlockDailyLimitWithDuration) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
   ScreenLockerTester().Lock();
 
@@ -501,7 +498,7 @@
 }
 
 // Tests the default time window limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_DefaultBedtime) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DefaultBedtime) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -564,7 +561,7 @@
 }
 
 // Tests the default time window limit.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_DefaultDailyLimit) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DefaultDailyLimit) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 GMT");
   ScreenLockerTester().Lock();
 
@@ -630,8 +627,7 @@
 }
 
 // Tests that the bedtime locks an active session when it is reached.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_ActiveSessionBedtime) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, ActiveSessionBedtime) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
 
   system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
@@ -669,8 +665,7 @@
 }
 
 // Tests that the daily limit locks the device when it is reached.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_ActiveSessionDailyLimit) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, ActiveSessionDailyLimit) {
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
 
   system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
@@ -706,8 +701,7 @@
 }
 
 // Tests bedtime during timezone changes.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_BedtimeOnTimezoneChange) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, BedtimeOnTimezoneChange) {
   LogInChildAndSetupClockWithTime("3 Jan 2018 10:00:00 GMT-0600");
   ScreenLockerTester().Lock();
 
@@ -759,7 +753,7 @@
 
 // Tests bedtime during timezone changes that make the clock go back in time.
 IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest,
-                       DISABLED_BedtimeOnEastToWestTimezoneChanges) {
+                       BedtimeOnEastToWestTimezoneChanges) {
   LogInChildAndSetupClockWithTime("3 Jan 2018 8:00:00 GMT+1300");
   ScreenLockerTester().Lock();
 
@@ -803,7 +797,7 @@
 }
 
 // Tests if call the observers for usage time limit warning.
-IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, DISABLED_CallObservers) {
+IN_PROC_BROWSER_TEST_P(ScreenTimeControllerTest, CallObservers) {
   if (!is_feature_enabled_)
     return;
   LogInChildAndSetupClockWithTime("1 Jan 2018 10:00:00 PST");
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index 7be4e595..c9ef91f 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -708,10 +708,19 @@
     const LoginScreenContext& context) {
   VLOG(1) << "SkipToLoginForTesting.";
   StartupUtils::MarkEulaAccepted();
+
+  // Enable metrics and crash collection, and verify that they're enabled.
   ChangeMetricsReportingStateWithReply(
       true,
       base::BindRepeating(&WizardController::OnChangedMetricsReportingState,
                           weak_factory_.GetWeakPtr()));
+  if (!StatsReportingController::Get()->IsEnabled()) {
+    LOG(ERROR) << "StatsReportingController reports collection is NOT enabled";
+  }
+  if (!crash_reporter::GetUploadsEnabled()) {
+    LOG(ERROR) << "crash_reporter reports that crash uploads NOT enabled";
+  }
+
   PerformPostEulaActions();
   OnDeviceDisabledChecked(false /* device_disabled */);
 }
diff --git a/chrome/browser/chromeos/policy/policy_certs_browsertest.cc b/chrome/browser/chromeos/policy/policy_certs_browsertest.cc
index f065626..762169e 100644
--- a/chrome/browser/chromeos/policy/policy_certs_browsertest.cc
+++ b/chrome/browser/chromeos/policy/policy_certs_browsertest.cc
@@ -362,14 +362,11 @@
   return result;
 }
 
-void IsCertInNSSDatabaseOnIOThreadWithCertDb(
+void IsCertInNSSDatabaseOnIOThreadWithCertList(
     const std::string& subject_common_name,
     bool* out_system_slot_available,
     base::OnceClosure done_closure,
-    net::NSSCertDatabase* cert_db) {
-  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
-  base::ScopedAllowBlockingForTesting scoped_allow_blocking_for_testing;
-  net::ScopedCERTCertificateList certs = cert_db->ListCertsSync();
+    net::ScopedCERTCertificateList certs) {
   for (const net::ScopedCERTCertificate& cert : certs) {
     if (HasSubjectCommonName(cert.get(), subject_common_name)) {
       *out_system_slot_available = true;
@@ -379,6 +376,17 @@
   std::move(done_closure).Run();
 }
 
+void IsCertInNSSDatabaseOnIOThreadWithCertDb(
+    const std::string& subject_common_name,
+    bool* out_system_slot_available,
+    base::OnceClosure done_closure,
+    net::NSSCertDatabase* cert_db) {
+  DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  cert_db->ListCerts(base::BindOnce(
+      &IsCertInNSSDatabaseOnIOThreadWithCertList, subject_common_name,
+      out_system_slot_available, std::move(done_closure)));
+}
+
 void IsCertInNSSDatabaseOnIOThread(content::ResourceContext* resource_context,
                                    const std::string& subject_common_name,
                                    bool* out_cert_found,
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
index b2b3fc8..b2ab273 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos_browsertest.cc
@@ -10,7 +10,6 @@
 #include "base/stl_util.h"
 #include "base/test/scoped_feature_list.h"
 #include "base/values.h"
-#include "build/buildflag.h"
 #include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/profiles/profile.h"
@@ -21,7 +20,6 @@
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/common/chrome_features.h"
 #include "chrome/test/base/mixin_based_in_process_browser_test.h"
-#include "chromeos/assistant/buildflags.h"
 #include "chromeos/constants/chromeos_switches.h"
 #include "components/arc/arc_features.h"
 #include "components/arc/arc_prefs.h"
@@ -228,15 +226,7 @@
 
 using UserCloudPolicyManagerChildTest = UserCloudPolicyManagerTest;
 
-// TODO(https://crbug.com/1005454): This test is failing on bots that show the
-// assistent opt-in screen.
-#if BUILDFLAG(ENABLE_CROS_LIBASSISTANT)
-#define MAYBE_PolicyForChildUser DISABLED_PolicyForChildUser
-#else
-#define MAYBE_PolicyForChildUser PolicyForChildUser
-#endif
-IN_PROC_BROWSER_TEST_P(UserCloudPolicyManagerChildTest,
-                       MAYBE_PolicyForChildUser) {
+IN_PROC_BROWSER_TEST_P(UserCloudPolicyManagerChildTest, PolicyForChildUser) {
   policy::BrowserPolicyConnector::SetNonEnterpriseDomainForTesting(
       "example.com");
   EXPECT_TRUE(policy::BrowserPolicyConnector::IsNonEnterpriseUser(
diff --git a/chrome/browser/content_index/content_index_provider_impl.cc b/chrome/browser/content_index/content_index_provider_impl.cc
index 99ef6c6..e4e4d85 100644
--- a/chrome/browser/content_index/content_index_provider_impl.cc
+++ b/chrome/browser/content_index/content_index_provider_impl.cc
@@ -101,7 +101,7 @@
   item.description = entry.description->description;
   item.filter = CategoryToFilter(entry.description->category);
   item.is_transient = false;
-  item.is_suggested = false;
+  item.is_suggested = true;
   item.creation_time = entry.registration_time;
   item.is_openable = true;
   item.state = offline_items_collection::OfflineItemState::COMPLETE;
diff --git a/chrome/browser/content_index/content_index_provider_unittest.cc b/chrome/browser/content_index/content_index_provider_unittest.cc
index 93316e2..60223b1 100644
--- a/chrome/browser/content_index/content_index_provider_unittest.cc
+++ b/chrome/browser/content_index/content_index_provider_unittest.cc
@@ -83,6 +83,7 @@
   EXPECT_FALSE(item.title.empty());
   EXPECT_FALSE(item.description.empty());
   EXPECT_FALSE(item.is_transient);
+  EXPECT_TRUE(item.is_suggested);
   EXPECT_TRUE(item.is_openable);
   EXPECT_EQ(item.page_url, kLaunchURL);
 }
diff --git a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
index 495b98b..ba3864ffb 100644
--- a/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
+++ b/chrome/browser/data_reduction_proxy/data_reduction_proxy_browsertest.cc
@@ -349,7 +349,6 @@
 
   ClientConfig config_;
   std::unique_ptr<ScopedConfigWaiter> config_waiter_;
-  base::test::ScopedFeatureList param_feature_list_;
   net::EmbeddedTestServer secure_proxy_check_server_;
   net::EmbeddedTestServer config_server_;
   std::unique_ptr<net::test_server::ControllableHttpResponse> favicon_catcher_;
@@ -1228,12 +1227,22 @@
       BYPASS_EVENT_TYPE_STATUS_502_HTTP_BAD_GATEWAY, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(DataReductionProxyFallbackBrowsertest,
-                       ProxyShortBypassedOn502ErrorWithFeature) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      features::kDataReductionProxyBlockOnBadGatewayResponse,
-      {{"block_duration_seconds", "10"}});
+class DataReductionProxyFallbackBrowsertestWithBlockOnBadGatewayFeature
+    : public DataReductionProxyFallbackBrowsertest {
+ public:
+  DataReductionProxyFallbackBrowsertestWithBlockOnBadGatewayFeature() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        features::kDataReductionProxyBlockOnBadGatewayResponse,
+        {{"block_duration_seconds", "10"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    DataReductionProxyFallbackBrowsertestWithBlockOnBadGatewayFeature,
+    ProxyShortBypassedOn502ErrorWithFeature) {
   base::HistogramTester histogram_tester;
   net::EmbeddedTestServer test_server;
   test_server.RegisterRequestHandler(
@@ -1284,13 +1293,14 @@
       : via_header_(std::get<1>(GetParam()) ? "1.1 Chrome-Compression-Proxy"
                                             : "bad"),
         primary_server_(GetTestServerType()),
-        secondary_server_(GetTestServerType()) {}
-
-  void SetUpOnMainThread() override {
+        secondary_server_(GetTestServerType()) {
     if (!std::get<2>(GetParam())) {
       scoped_feature_list_.InitAndDisableFeature(
           features::kDataReductionProxyDisableProxyFailedWarmup);
     }
+  }
+
+  void SetUpOnMainThread() override {
     primary_server_loop_ = std::make_unique<base::RunLoop>();
     primary_server_.RegisterRequestHandler(base::BindRepeating(
         &DataReductionProxyWarmupURLBrowsertest::WaitForWarmupRequest,
diff --git a/chrome/browser/download/offline_item_utils.cc b/chrome/browser/download/offline_item_utils.cc
index 061d88d..1032344 100644
--- a/chrome/browser/download/offline_item_utils.cc
+++ b/chrome/browser/download/offline_item_utils.cc
@@ -103,6 +103,7 @@
   item.total_size_bytes = download_item->GetTotalBytes();
   item.externally_removed = download_item->GetFileExternallyRemoved();
   item.creation_time = download_item->GetStartTime();
+  item.completion_time = download_item->GetEndTime();
   item.last_accessed_time = download_item->GetLastAccessTime();
   item.is_openable = download_item->CanOpenDownload();
   item.file_path = download_item->GetTargetFilePath();
diff --git a/chrome/browser/download/offline_item_utils_unittest.cc b/chrome/browser/download/offline_item_utils_unittest.cc
index 5f93479..a9ae526 100644
--- a/chrome/browser/download/offline_item_utils_unittest.cc
+++ b/chrome/browser/download/offline_item_utils_unittest.cc
@@ -122,6 +122,7 @@
   base::FilePath file_name(FILE_PATH_LITERAL("image.png"));
   std::string mime_type = "image/png";
   base::Time creation_time = base::Time::Now();
+  base::Time completion_time = base::Time::Now();
   base::Time last_access_time = base::Time::Now();
   download::DownloadInterruptReason interrupt_reason =
       download::DOWNLOAD_INTERRUPT_REASON_NONE;
@@ -149,6 +150,7 @@
   ON_CALL(*download, AllowMetered()).WillByDefault(Return(allow_metered));
   ON_CALL(*download, GetReceivedBytes()).WillByDefault(Return(received_bytes));
   ON_CALL(*download, GetTotalBytes()).WillByDefault(Return(total_bytes));
+  ON_CALL(*download, GetEndTime()).WillByDefault(Return(completion_time));
 
   ON_CALL(*download, TimeRemaining(_))
       .WillByDefault(testing::DoAll(
@@ -173,6 +175,7 @@
   EXPECT_EQ(total_bytes, offline_item.total_size_bytes);
   EXPECT_EQ(externally_removed, offline_item.externally_removed);
   EXPECT_EQ(creation_time, offline_item.creation_time);
+  EXPECT_EQ(completion_time, offline_item.completion_time);
   EXPECT_EQ(last_access_time, offline_item.last_accessed_time);
   EXPECT_EQ(is_openable, offline_item.is_openable);
   EXPECT_EQ(file_path, offline_item.file_path);
diff --git a/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc b/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc
index 881f36c6..e6460b9 100644
--- a/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc
+++ b/chrome/browser/extensions/api/signed_in_devices/id_mapping_helper_unittest.cc
@@ -33,13 +33,15 @@
 
   devices.push_back(std::make_unique<DeviceInfo>(
       base::GenerateGUID(), "abc Device", "XYZ v1", "XYZ SyncAgent v1",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id1", base::Time(),
+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id1",
+      base::SysInfo::HardwareInfo(), base::Time(),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt));
 
   devices.push_back(std::make_unique<DeviceInfo>(
       base::GenerateGUID(), "def Device", "XYZ v1", "XYZ SyncAgent v1",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id2", base::Time(),
+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id2",
+      base::SysInfo::HardwareInfo(), base::Time(),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt));
 
@@ -58,7 +60,8 @@
   // Now add a third device.
   devices.push_back(std::make_unique<DeviceInfo>(
       base::GenerateGUID(), "ghi Device", "XYZ v1", "XYZ SyncAgent v1",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id3", base::Time(),
+      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id3",
+      base::SysInfo::HardwareInfo(), base::Time(),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt));
 
diff --git a/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc b/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc
index 937e209..0ebd073 100644
--- a/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc
+++ b/chrome/browser/extensions/api/signed_in_devices/signed_in_devices_api_unittest.cc
@@ -40,17 +40,19 @@
   scoped_refptr<Extension> extension_test =
       extension_prefs.AddExtension(extension_name);
 
-  DeviceInfo device_info1(
-      base::GenerateGUID(), "abc Device", "XYZ v1", "XYZ SyncAgent v1",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id", base::Time(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  DeviceInfo device_info1(base::GenerateGUID(), "abc Device", "XYZ v1",
+                          "XYZ SyncAgent v1",
+                          sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+                          base::SysInfo::HardwareInfo(), base::Time(),
+                          /*send_tab_to_self_receiving_enabled=*/true,
+                          /*sharing_info=*/base::nullopt);
 
-  DeviceInfo device_info2(
-      base::GenerateGUID(), "def Device", "XYZ v2", "XYZ SyncAgent v2",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id", base::Time(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  DeviceInfo device_info2(base::GenerateGUID(), "def Device", "XYZ v2",
+                          "XYZ SyncAgent v2",
+                          sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+                          base::SysInfo::HardwareInfo(), base::Time(),
+                          /*send_tab_to_self_receiving_enabled=*/true,
+                          /*sharing_info=*/base::nullopt);
 
   device_tracker.Add(&device_info1);
   device_tracker.Add(&device_info2);
@@ -67,11 +69,12 @@
 
   // Add a third device and make sure the first 2 ids are retained and a new
   // id is generated for the third device.
-  DeviceInfo device_info3(
-      base::GenerateGUID(), "def Device", "jkl v2", "XYZ SyncAgent v2",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id", base::Time(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  DeviceInfo device_info3(base::GenerateGUID(), "def Device", "jkl v2",
+                          "XYZ SyncAgent v2",
+                          sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+                          base::SysInfo::HardwareInfo(), base::Time(),
+                          /*send_tab_to_self_receiving_enabled=*/true,
+                          /*sharing_info=*/base::nullopt);
 
   device_tracker.Add(&device_info3);
 
@@ -133,17 +136,19 @@
       DeviceInfoSyncServiceFactory::GetForProfile(profile())
           ->GetDeviceInfoTracker());
 
-  DeviceInfo device_info1(
-      base::GenerateGUID(), "abc Device", "XYZ v1", "XYZ SyncAgent v1",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id", base::Time(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  DeviceInfo device_info1(base::GenerateGUID(), "abc Device", "XYZ v1",
+                          "XYZ SyncAgent v1",
+                          sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+                          base::SysInfo::HardwareInfo(), base::Time(),
+                          /*send_tab_to_self_receiving_enabled=*/true,
+                          /*sharing_info=*/base::nullopt);
 
-  DeviceInfo device_info2(
-      base::GenerateGUID(), "def Device", "XYZ v2", "XYZ SyncAgent v2",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id", base::Time(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  DeviceInfo device_info2(base::GenerateGUID(), "def Device", "XYZ v2",
+                          "XYZ SyncAgent v2",
+                          sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+                          base::SysInfo::HardwareInfo(), base::Time(),
+                          /*send_tab_to_self_receiving_enabled=*/true,
+                          /*sharing_info=*/base::nullopt);
 
   device_tracker->Add(&device_info1);
   device_tracker->Add(&device_info2);
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 46849eb..2218134 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1032,11 +1032,6 @@
     "expiry_milestone": 82
   },
   {
-    "name": "enable-avoid-flash-between-navigation",
-    "owners": [ "schenney", "paint-dev" ],
-    "expiry_milestone": 80
-  },
-  {
     "name": "enable-backdrop-filter",
     "owners": [ "masonfreed", "paint-dev@chromium.org" ],
     "expiry_milestone": 78
@@ -1141,7 +1136,7 @@
   {
     "name": "enable-cups-printers-ui-overhaul",
     "owners": [ "jimmyxgong" ],
-    "expiry_milestone": 79
+    "expiry_milestone": 81
   },
   {
     "name": "enable-custom-mac-paper-sizes",
@@ -1521,6 +1516,11 @@
     "owners": [ "enne", "khushalsagar" ],
     "expiry_milestone": 80
   },
+    {
+    "name": "enable-paint-holding",
+    "owners": [ "schenney", "paint-dev" ],
+    "expiry_milestone": 80
+  },
   {
     "name": "enable-parallel-downloading",
     "owners": [ "qinmin", "xingliu", "dtrainor" ],
@@ -2451,6 +2451,11 @@
     "expiry_milestone": 81
   },
   {
+    "name": "metal",
+    "owners": ["ccameron", "jvanverth"],
+    "expiry_milestone": 83
+  },
+  {
     "name": "mime-handler-view-in-cross-process-frame",
     "owners": [ "ekaramad" ],
     "expiry_milestone": 80
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 593f6f1b..399f067 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -3120,6 +3120,11 @@
     "the Omnibox, which, when clicked, displays a bubble with information on "
     "how to toggle Chrome's system-level media permissions.";
 
+const char kMetalName[] = "Metal";
+const char kMetalDescription[] =
+    "Use Metal instead of OpenGL for rasterization (if out-of-process "
+    "rasterization is enabled) and display (if the Skia renderer is enabled)";
+
 #endif
 
 // Chrome OS -------------------------------------------------------------------
@@ -3894,6 +3899,13 @@
 
 #endif  // defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
+const char kPaintHoldingName[] =
+    "Delay the commit to screen for same-origin navigations";
+const char kPaintHoldingDescription[] =
+    "Enables a delay before commiting the page to screen when navigating "
+    "between pages in the same origin. This may help avoid a flash of unstyled "
+    "content for same-origin navigations";
+
 #if defined(WEBRTC_USE_PIPEWIRE)
 
 extern const char kWebrtcPipeWireCapturerName[] = "WebRTC PipeWire support";
@@ -3903,13 +3915,6 @@
 
 #endif  // #if defined(WEBRTC_USE_PIPEWIRE)
 
-const char kAvoidFlashBetweenNavigationName[] =
-    "Enable flash avoidance between same-origin navigations";
-const char kAvoidFlahsBetweenNavigationDescription[] =
-    "Enables experimental flash avoidance when navigating between pages "
-    "in the same origin. This feature is in the implementation stages and "
-    "currently has no effect.";
-
 // ============================================================================
 // Don't just add flags to the end, put them in the right section in
 // alphabetical order just like the header file.
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 48e4631..31a5fc3 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -1832,6 +1832,9 @@
 extern const char kMacSystemMediaPermissionsInfoUiName[];
 extern const char kMacSystemMediaPermissionsInfoUiDescription[];
 
+extern const char kMetalName[];
+extern const char kMetalDescription[];
+
 // Non-Mac --------------------------------------------------------------------
 
 #else  // !defined(OS_MACOSX)
@@ -2354,8 +2357,8 @@
 
 #endif  // defined(TOOLKIT_VIEWS) || defined(OS_ANDROID)
 
-extern const char kAvoidFlashBetweenNavigationName[];
-extern const char kAvoidFlahsBetweenNavigationDescription[];
+extern const char kPaintHoldingName[];
+extern const char kPaintHoldingDescription[];
 
 #if defined(WEBRTC_USE_PIPEWIRE)
 
diff --git a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
index deae569..d6a07a1 100644
--- a/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
+++ b/chrome/browser/navigation_predictor/navigation_predictor_browsertest.cc
@@ -432,16 +432,24 @@
                                     3);
 }
 
-// Test that we preconnect after the last preconnect timed out.
-IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
-                       DISABLE_ON_CHROMEOS(ActionAccuracy_timeout)) {
-  base::HistogramTester histogram_tester;
+class NavigationPredictorBrowserTestWithUnusedIdleSocketTimeout
+    : public NavigationPredictorBrowserTest {
+ public:
+  NavigationPredictorBrowserTestWithUnusedIdleSocketTimeout() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        net::features::kNetUnusedIdleSocketTimeout,
+        {{"unused_idle_socket_timeout_seconds", "0"}});
+  }
 
-  base::test::ScopedFeatureList scoped_feature_list;
-  std::map<std::string, std::string> parameters;
-  parameters["unused_idle_socket_timeout_seconds"] = "0";
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      net::features::kNetUnusedIdleSocketTimeout, parameters);
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+// Test that we preconnect after the last preconnect timed out.
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorBrowserTestWithUnusedIdleSocketTimeout,
+    DISABLE_ON_CHROMEOS(ActionAccuracy_timeout)) {
+  base::HistogramTester histogram_tester;
 
   const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
   ui_test_utils::NavigateToURL(browser(), url);
@@ -459,23 +467,31 @@
                        NavigationPredictor::Action::kPreconnectAfterTimeout)));
 }
 
+class NavigationPredictorBrowserTestWithNegativePredictorRetryPreconnect
+    : public NavigationPredictorBrowserTest {
+ public:
+  NavigationPredictorBrowserTestWithNegativePredictorRetryPreconnect() {
+    // -1 would force synchronous retries if retries were not disabled.
+    net_feature_list_.InitAndEnableFeatureWithParameters(
+        net::features::kNetUnusedIdleSocketTimeout,
+        {{"unused_idle_socket_timeout_seconds", "-1"}});
+    predictor_feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kNavigationPredictor,
+        {{"retry_preconnect_wait_time_ms", "-1"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList net_feature_list_;
+  base::test::ScopedFeatureList predictor_feature_list_;
+};
+
 // Test that we don't preconnect after the last preconnect timed out when
 // retry_preconnect_wait_time_ms is negative.
-IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
-                       DISABLE_ON_CHROMEOS(ActionAccuracy_timeout_no_retry)) {
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorBrowserTestWithNegativePredictorRetryPreconnect,
+    DISABLE_ON_CHROMEOS(ActionAccuracy_timeout_no_retry)) {
   base::HistogramTester histogram_tester;
 
-  base::test::ScopedFeatureList scoped_feature_list_net;
-  // -1 would force synchronous retries if retries were not disabled.
-  scoped_feature_list_net.InitAndEnableFeatureWithParameters(
-      net::features::kNetUnusedIdleSocketTimeout,
-      {{"unused_idle_socket_timeout_seconds", "-1"}});
-
-  base::test::ScopedFeatureList scoped_feature_list_predictor;
-  scoped_feature_list_predictor.InitAndEnableFeatureWithParameters(
-      blink::features::kNavigationPredictor,
-      {{"retry_preconnect_wait_time_ms", "-1"}});
-
   const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
   ui_test_utils::NavigateToURL(browser(), url);
   WaitForLayout();
@@ -486,18 +502,25 @@
                        NavigationPredictor::Action::kPreconnectAfterTimeout)));
 }
 
+class NavigationPredictorBrowserTestWithDefaultPredictorEnabled
+    : public NavigationPredictorBrowserTest {
+ public:
+  NavigationPredictorBrowserTestWithDefaultPredictorEnabled() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kNavigationPredictor, {});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Test that the action accuracy is properly recorded and when same origin
 // preconnections are enabled, then navigation predictor initiates the
 // preconnection.
 IN_PROC_BROWSER_TEST_F(
-    NavigationPredictorBrowserTest,
+    NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
     DISABLE_ON_CHROMEOS(
         ActionAccuracy_DifferentOrigin_VisibilityChangedPreconnectEnabled)) {
-  std::map<std::string, std::string> parameters;
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(
-      blink::features::kNavigationPredictor, parameters);
-
   base::HistogramTester histogram_tester;
 
   const GURL& url = GetTestURL("/page_with_same_host_anchor_element.html");
@@ -542,15 +565,22 @@
       NavigationPredictor::Action::kPreconnectOnVisibilityChange, 2);
 }
 
-IN_PROC_BROWSER_TEST_F(
-    NavigationPredictorBrowserTest,
-    DISABLE_ON_CHROMEOS(NoPreconnectNonSearchOnOtherHostLinks)) {
-  std::map<std::string, std::string> parameters;
-  base::test::ScopedFeatureList feature_list;
-  parameters["preconnect_skip_link_scores"] = "false";
-  feature_list.InitAndEnableFeatureWithParameters(
-      blink::features::kNavigationPredictor, parameters);
+class NavigationPredictorBrowserTestNoSkipLinkScores
+    : public NavigationPredictorBrowserTest {
+ public:
+  NavigationPredictorBrowserTestNoSkipLinkScores() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kNavigationPredictor,
+        {{"preconnect_skip_link_scores", "false"}});
+  }
 
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorBrowserTestNoSkipLinkScores,
+    DISABLE_ON_CHROMEOS(NoPreconnectNonSearchOnOtherHostLinks)) {
   base::HistogramTester histogram_tester;
 
   // This page only has non-same host links.
@@ -563,13 +593,9 @@
       NavigationPredictor::Action::kNone, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
-                       DISABLE_ON_CHROMEOS(PreconnectNonSearch)) {
-  std::map<std::string, std::string> parameters;
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(
-      blink::features::kNavigationPredictor, parameters);
-
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
+    DISABLE_ON_CHROMEOS(PreconnectNonSearch)) {
   base::HistogramTester histogram_tester;
 
   // This page only has non-same host links.
@@ -582,8 +608,22 @@
       NavigationPredictor::Action::kPreconnect, 1);
 }
 
-IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
-                       DISABLE_ON_CHROMEOS(PrefetchAfterPreconnect)) {
+class NavigationPredictorBrowserTestWithPrefetchAfterPreconnect
+    : public NavigationPredictorBrowserTest {
+ public:
+  NavigationPredictorBrowserTestWithPrefetchAfterPreconnect() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        blink::features::kNavigationPredictor,
+        {{"prefetch_after_preconnect", "true"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorBrowserTestWithPrefetchAfterPreconnect,
+    DISABLE_ON_CHROMEOS(PrefetchAfterPreconnect)) {
   prerender::PrerenderManager::SetMode(
       prerender::PrerenderManager::PRERENDER_MODE_NOSTATE_PREFETCH);
 
@@ -591,12 +631,6 @@
   std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder =
       std::make_unique<ukm::TestAutoSetUkmRecorder>();
 
-  std::map<std::string, std::string> parameters;
-  base::test::ScopedFeatureList feature_list;
-  parameters["prefetch_after_preconnect"] = "true";
-  feature_list.InitAndEnableFeatureWithParameters(
-      blink::features::kNavigationPredictor, parameters);
-
   base::HistogramTester histogram_tester;
 
   ui_test_utils::NavigateToURL(browser(), url);
@@ -632,18 +666,13 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
-                       DISABLE_ON_CHROMEOS(NoPreconnectSearch)) {
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
+    DISABLE_ON_CHROMEOS(NoPreconnectSearch)) {
   static const char kShortName[] = "test";
   static const char kSearchURL[] =
       "/anchors_different_area.html?q={searchTerms}";
 
-  // Force Preconnect on
-  std::map<std::string, std::string> parameters;
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(
-      blink::features::kNavigationPredictor, parameters);
-
   // Set up default search engine.
   TemplateURLService* model =
       TemplateURLServiceFactory::GetForProfile(browser()->profile());
@@ -1025,8 +1054,9 @@
 }
 
 // Verify that the observers are notified of predictions on search results page.
-IN_PROC_BROWSER_TEST_F(NavigationPredictorBrowserTest,
-                       DISABLE_ON_CHROMEOS(ObserverNotifiedOnSearchPage)) {
+IN_PROC_BROWSER_TEST_F(
+    NavigationPredictorBrowserTestWithDefaultPredictorEnabled,
+    DISABLE_ON_CHROMEOS(ObserverNotifiedOnSearchPage)) {
   TestObserver observer;
 
   NavigationPredictorKeyedService* service =
@@ -1038,12 +1068,6 @@
   static const char kSearchURL[] =
       "/anchors_different_area.html?q={searchTerms}";
 
-  // Force Preconnect on
-  std::map<std::string, std::string> parameters;
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeatureWithParameters(
-      blink::features::kNavigationPredictor, parameters);
-
   // Set up default search engine.
   TemplateURLService* model =
       TemplateURLServiceFactory::GetForProfile(browser()->profile());
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
index 229ae68..18b467f 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager.cc
@@ -472,13 +472,9 @@
 void OptimizationGuideHintsManager::FetchTopHostsHints() {
   DCHECK(top_host_provider_);
 
-  size_t max_hints_to_fetch = optimization_guide::features::
-      MaxHostsForOptimizationGuideServiceHintsFetch();
-  std::vector<std::string> top_hosts =
-      top_host_provider_->GetTopHosts(max_hints_to_fetch);
+  std::vector<std::string> top_hosts = top_host_provider_->GetTopHosts();
   if (top_hosts.empty())
     return;
-  DCHECK_GE(max_hints_to_fetch, top_hosts.size());
 
   if (!hints_fetcher_) {
     hints_fetcher_ = std::make_unique<optimization_guide::HintsFetcher>(
@@ -636,11 +632,6 @@
   for (const auto& url : prediction->sorted_predicted_urls()) {
     if (!IsAllowedToFetchNavigationHints(url))
       continue;
-    if (target_hosts.size() >=
-        optimization_guide::features::
-            MaxHostsForOptimizationGuideServiceHintsFetch()) {
-      break;
-    }
 
     // Insert the host to |target_hosts|. The host is inserted to
     // |target_hosts_serialized| only if it was not a duplicate insertion to
diff --git a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
index b3fb5fbf07..3ad9eb9 100644
--- a/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_hints_manager_unittest.cc
@@ -130,22 +130,10 @@
   explicit FakeTopHostProvider(std::vector<std::string> top_hosts)
       : top_hosts_(top_hosts) {}
 
-  std::vector<std::string> GetTopHosts(size_t max_sites) override {
+  std::vector<std::string> GetTopHosts() override {
     num_top_hosts_called_++;
 
-    if (top_hosts_.size() <= max_sites) {
       return top_hosts_;
-    }
-
-    std::vector<std::string> top_hosts;
-    top_hosts.reserve(max_sites);
-    for (const auto& top_host : top_hosts_) {
-      if (top_hosts.size() >= max_sites)
-        return top_hosts;
-
-      top_hosts.push_back(top_host);
-    }
-    return top_hosts;
   }
 
   int get_num_top_hosts_called() const { return num_top_hosts_called_; }
diff --git a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
index 2c656ab5..cea83a5 100644
--- a/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_keyed_service_browsertest.cc
@@ -102,26 +102,40 @@
 
 }  // namespace
 
-using OptimizationGuideKeyedServiceDisabledBrowserTest = InProcessBrowserTest;
+class OptimizationGuideKeyedServiceDisabledBrowserTest
+    : public InProcessBrowserTest {
+ public:
+  OptimizationGuideKeyedServiceDisabledBrowserTest() {
+    feature_list_.InitWithFeatures(
+        {optimization_guide::features::kOptimizationHints},
+        {optimization_guide::features::kOptimizationGuideKeyedService});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
 
 IN_PROC_BROWSER_TEST_F(OptimizationGuideKeyedServiceDisabledBrowserTest,
                        KeyedServiceNotEnabledButOptimizationHintsEnabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {optimization_guide::features::kOptimizationHints},
-      {optimization_guide::features::kOptimizationGuideKeyedService});
-
   EXPECT_EQ(nullptr, OptimizationGuideKeyedServiceFactory::GetForProfile(
                          browser()->profile()));
 }
 
-IN_PROC_BROWSER_TEST_F(OptimizationGuideKeyedServiceDisabledBrowserTest,
-                       KeyedServiceEnabledButOptimizationHintsDisabled) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeatures(
-      {optimization_guide::features::kOptimizationGuideKeyedService},
-      {optimization_guide::features::kOptimizationHints});
+class OptimizationGuideKeyedServiceHintsDisabledBrowserTest
+    : public InProcessBrowserTest {
+ public:
+  OptimizationGuideKeyedServiceHintsDisabledBrowserTest() {
+    feature_list_.InitWithFeatures(
+        {optimization_guide::features::kOptimizationGuideKeyedService},
+        {optimization_guide::features::kOptimizationHints});
+  }
 
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(OptimizationGuideKeyedServiceHintsDisabledBrowserTest,
+                       KeyedServiceEnabledButOptimizationHintsDisabled) {
   EXPECT_EQ(nullptr, OptimizationGuideKeyedServiceFactory::GetForProfile(
                          browser()->profile()));
 }
@@ -129,18 +143,15 @@
 class OptimizationGuideKeyedServiceBrowserTest
     : public OptimizationGuideKeyedServiceDisabledBrowserTest {
  public:
-  OptimizationGuideKeyedServiceBrowserTest() = default;
-  ~OptimizationGuideKeyedServiceBrowserTest() override = default;
-
-  void SetUp() override {
+  OptimizationGuideKeyedServiceBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
         {optimization_guide::features::kOptimizationHints,
          optimization_guide::features::kOptimizationGuideKeyedService},
         {});
-
-    OptimizationGuideKeyedServiceDisabledBrowserTest::SetUp();
   }
 
+  ~OptimizationGuideKeyedServiceBrowserTest() override = default;
+
   void SetUpCommandLine(base::CommandLine* cmd) override {
     cmd->AppendSwitch(optimization_guide::switches::kPurgeHintCacheStore);
   }
@@ -167,12 +178,6 @@
         net::EffectiveConnectionType::EFFECTIVE_CONNECTION_TYPE_SLOW_2G);
   }
 
-  void TearDown() override {
-    scoped_feature_list_.Reset();
-
-    OptimizationGuideKeyedServiceDisabledBrowserTest::TearDown();
-  }
-
   void TearDownOnMainThread() override {
     EXPECT_TRUE(https_server_->ShutdownAndWaitUntilComplete());
 
@@ -624,9 +629,10 @@
       keyed_service->GetTopHostProvider();
   ASSERT_TRUE(top_host_provider);
 
-  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts(1);
-  EXPECT_EQ(1ul, top_hosts.size());
+  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts();
+  EXPECT_EQ(2ul, top_hosts.size());
   EXPECT_EQ("myfavoritesite.com", top_hosts[0]);
+  EXPECT_EQ("myotherfavoritesite.com", top_hosts[1]);
 }
 
 class OptimizationGuideKeyedServiceCommandLineOverridesTest
@@ -653,7 +659,8 @@
       keyed_service->GetTopHostProvider();
   ASSERT_TRUE(top_host_provider);
 
-  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts(1);
-  EXPECT_EQ(1ul, top_hosts.size());
+  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts();
+  EXPECT_EQ(2ul, top_hosts.size());
   EXPECT_EQ("whatever.com", top_hosts[0]);
+  EXPECT_EQ("somehost.com", top_hosts[1]);
 }
diff --git a/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc b/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc
index 1cca7e6..6b190478 100644
--- a/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_top_host_provider.cc
@@ -219,8 +219,7 @@
       static_cast<int>(new_state));
 }
 
-std::vector<std::string> OptimizationGuideTopHostProvider::GetTopHosts(
-    size_t max_sites) {
+std::vector<std::string> OptimizationGuideTopHostProvider::GetTopHosts() {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(browser_context_);
@@ -269,7 +268,6 @@
   }
 
   std::vector<std::string> top_hosts;
-  top_hosts.reserve(max_sites);
 
   // Create a vector of the top hosts by engagement score up to |max_sites|
   // size. Currently utilizes just the first |max_sites| entries. Only HTTPS
@@ -293,10 +291,6 @@
       (time_clock_->Now() - blacklist_initialized_time);
 
   for (const auto& detail : engagement_details) {
-    if (top_hosts.size() >= max_sites)
-      return top_hosts;
-    // TODO(b/968542): Skip origins that are local hosts (e.g., IP addresses,
-    // localhost:8080 etc.).
     if (!detail.origin.SchemeIs(url::kHttpsScheme))
       continue;
     // Once the engagement score is less than the initial engagement score for a
diff --git a/chrome/browser/optimization_guide/optimization_guide_top_host_provider.h b/chrome/browser/optimization_guide/optimization_guide_top_host_provider.h
index d72517fd..50210f4 100644
--- a/chrome/browser/optimization_guide/optimization_guide_top_host_provider.h
+++ b/chrome/browser/optimization_guide/optimization_guide_top_host_provider.h
@@ -50,7 +50,7 @@
       content::NavigationHandle* navigation_handle);
 
   // optimization_guide::TopHostProvider implementation:
-  std::vector<std::string> GetTopHosts(size_t max_sites) override;
+  std::vector<std::string> GetTopHosts() override;
 
  private:
   // Initializes the HintsFetcherTopHostBlacklist with all the hosts in the site
diff --git a/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc b/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc
index b609baf9f..34cb547 100644
--- a/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc
+++ b/chrome/browser/optimization_guide/optimization_guide_top_host_provider_unittest.cc
@@ -257,13 +257,9 @@
   SetTopHostBlacklistState(optimization_guide::prefs::
                                HintsFetcherTopHostBlacklistState::kInitialized);
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 3;
   AddEngagedHosts(engaged_hosts);
 
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
-
-  EXPECT_EQ(max_top_hosts, hosts.size());
+  EXPECT_EQ(engaged_hosts, top_host_provider()->GetTopHosts().size());
 }
 
 TEST_F(OptimizationGuideTopHostProviderTest,
@@ -271,15 +267,12 @@
   SetTopHostBlacklistState(optimization_guide::prefs::
                                HintsFetcherTopHostBlacklistState::kInitialized);
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 5;
   size_t num_hosts_blacklisted = 2;
   AddEngagedHosts(engaged_hosts);
 
   PopulateTopHostBlacklist(num_hosts_blacklisted);
 
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
-
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), engaged_hosts - num_hosts_blacklisted);
 }
 
@@ -289,11 +282,9 @@
             optimization_guide::prefs::HintsFetcherTopHostBlacklistState::
                 kNotInitialized);
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 5;
   AddEngagedHosts(engaged_hosts);
 
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   // On initialization, GetTopHosts should always return zero hosts.
   EXPECT_EQ(hosts.size(), 0u);
   EXPECT_EQ(GetCurrentTopHostBlacklistState(),
@@ -304,19 +295,17 @@
 TEST_F(OptimizationGuideTopHostProviderTest,
        GetTopHostsBlacklistStateNotInitializedToInitialized) {
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 5;
   size_t num_hosts_blacklisted = 5;
   AddEngagedHosts(engaged_hosts);
 
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
   // Blacklist should now have items removed.
   size_t num_navigations = 2;
   RemoveHostsFromBlacklist(num_navigations);
 
-  hosts = top_host_provider()->GetTopHosts(max_top_hosts);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(),
             engaged_hosts - (num_hosts_blacklisted - num_navigations));
   EXPECT_EQ(GetCurrentTopHostBlacklistState(),
@@ -327,19 +316,17 @@
 TEST_F(OptimizationGuideTopHostProviderTest,
        GetTopHostsBlacklistStateNotInitializedToEmpty) {
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 5;
   size_t num_hosts_blacklisted = 5;
   AddEngagedHosts(engaged_hosts);
 
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
   // Blacklist should now have items removed.
   size_t num_navigations = 5;
   RemoveHostsFromBlacklist(num_navigations);
 
-  hosts = top_host_provider()->GetTopHosts(max_top_hosts);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(),
             engaged_hosts - (num_hosts_blacklisted - num_navigations));
   EXPECT_EQ(
@@ -350,32 +337,28 @@
 TEST_F(OptimizationGuideTopHostProviderTest,
        MaybeUpdateTopHostBlacklistNavigationsOnBlacklist) {
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 5;
   size_t num_top_hosts = 3;
   AddEngagedHosts(engaged_hosts);
 
   // The blacklist should be populated on the first request.
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
   // Navigate to some engaged hosts to trigger their removal from the top host
   // blacklist.
   SimulateUniqueNavigationsToTopHosts(num_top_hosts);
 
-  hosts = top_host_provider()->GetTopHosts(max_top_hosts);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), num_top_hosts);
 }
 
 TEST_F(OptimizationGuideTopHostProviderTest,
        MaybeUpdateTopHostBlacklistEmptyBlacklist) {
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 5;
   size_t num_top_hosts = 5;
   AddEngagedHosts(engaged_hosts);
 
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
   SimulateUniqueNavigationsToTopHosts(num_top_hosts);
@@ -384,14 +367,13 @@
       GetCurrentTopHostBlacklistState(),
       optimization_guide::prefs::HintsFetcherTopHostBlacklistState::kEmpty);
 
-  hosts = top_host_provider()->GetTopHosts(max_top_hosts);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), num_top_hosts);
 }
 
 TEST_F(OptimizationGuideTopHostProviderTest,
        HintsFetcherTopHostBlacklistNonHTTPOrHTTPSHost) {
   size_t engaged_hosts = 5;
-  size_t max_top_hosts = 5;
   size_t num_hosts_blacklisted = 5;
   GURL http_url = GURL("http://anyscheme.com");
   GURL file_url = GURL("file://anyscheme.com");
@@ -406,15 +388,14 @@
 
   // A Non HTTP/HTTPS navigation should not remove a host from the blacklist.
   SimulateNavigation(file_url);
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
   // The host, anyscheme.com, should still be on the blacklist.
   EXPECT_TRUE(IsHostBlacklisted(file_url.host()));
 
   // TopHostProviderImpl prevents HTTP hosts from being returned.
   SimulateNavigation(http_url);
-  hosts = top_host_provider()->GetTopHosts(max_top_hosts);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
   EXPECT_FALSE(IsHostBlacklisted(http_url.host()));
@@ -424,16 +405,13 @@
        IntializeTopHostBlacklistWithMaxTopSites) {
   size_t engaged_hosts =
       optimization_guide::features::MaxHintsFetcherTopHostBlacklistSize() + 1;
-  size_t max_top_hosts =
-      optimization_guide::features::MaxHintsFetcherTopHostBlacklistSize() + 1;
   AddEngagedHosts(engaged_hosts);
 
   // Blacklist should be populated on the first request.
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(max_top_hosts);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
-  hosts = top_host_provider()->GetTopHosts(max_top_hosts);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(
       hosts.size(),
       engaged_hosts -
@@ -457,11 +435,6 @@
   size_t engaged_hosts =
       optimization_guide::features::MaxHintsFetcherTopHostBlacklistSize() + 1;
 
-  // Set the count of maximum hosts requested to a large number so that the
-  // count itself does not prevent top_host_provider() from returning hosts that
-  // it would have otherwise returned.
-  static const size_t kMaxHostsRequested = INT16_MAX;
-
   AddEngagedHosts(engaged_hosts);
   // Add two hosts with very low engagement scores that should not be returned
   // by the top host provider.
@@ -470,11 +443,10 @@
 
   // Blacklist should be populated on the first request. Set the count of
   // desired
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(kMaxHostsRequested);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
-  hosts = top_host_provider()->GetTopHosts(kMaxHostsRequested);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(1u, hosts.size());
   EXPECT_EQ(GetCurrentTopHostBlacklistState(),
             optimization_guide::prefs::HintsFetcherTopHostBlacklistState::
@@ -495,7 +467,7 @@
   AddEngagedHost(GURL("https://lowengagement3.com"), 1);
   AddEngagedHost(GURL("https://lowengagement4.com"), 1);
 
-  hosts = top_host_provider()->GetTopHosts(kMaxHostsRequested);
+  hosts = top_host_provider()->GetTopHosts();
   // Four hosts lowengagement[1-4] should now be present in |hosts|.
   EXPECT_EQ(
       engaged_hosts + 4 -
@@ -522,11 +494,6 @@
   size_t engaged_hosts =
       optimization_guide::features::MaxHintsFetcherTopHostBlacklistSize() + 1;
 
-  // Set the count of maximum hosts requested to a large number so that the
-  // count itself does not prevent top_host_provider() from returning hosts that
-  // it would have otherwise returned.
-  static const size_t kMaxHostsRequested = INT16_MAX;
-
   AddEngagedHostsWithPoints(engaged_hosts, 15);
   // Add two hosts with engagement scores less than 15. These hosts should not
   // be returned by the top host provider because the minimum engagement score
@@ -540,11 +507,10 @@
             GetHintsFetcherDataSaverTopHostBlacklistMinimumEngagementScore());
 
   // Blacklist should be populated on the first request.
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(kMaxHostsRequested);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
-  hosts = top_host_provider()->GetTopHosts(kMaxHostsRequested);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_NEAR(GetHintsFetcherDataSaverTopHostBlacklistMinimumEngagementScore(),
               GetHintsFetcherDataSaverTopHostBlacklistMinimumEngagementScore(),
               1);
@@ -563,17 +529,11 @@
   size_t engaged_hosts =
       optimization_guide::features::MaxHintsFetcherTopHostBlacklistSize() - 2;
 
-  // Set the count of maximum hosts requested to a large number so that the
-  // count itself does not prevent top_host_provider() from returning hosts that
-  // it would have otherwise returned.
-  static const size_t kMaxHostsRequested = INT16_MAX;
-
   AddEngagedHostsWithPoints(engaged_hosts, 2);
 
   // Blacklist should be populated on the first request. Set the count of
   // desired
-  std::vector<std::string> hosts =
-      top_host_provider()->GetTopHosts(kMaxHostsRequested);
+  std::vector<std::string> hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(hosts.size(), 0u);
 
   // Add two hosts with very low engagement scores. These hosts should be
@@ -583,7 +543,7 @@
   AddEngagedHost(GURL("https://lowengagement1.com"), 1);
   AddEngagedHost(GURL("https://lowengagement2.com"), 1);
 
-  hosts = top_host_provider()->GetTopHosts(kMaxHostsRequested);
+  hosts = top_host_provider()->GetTopHosts();
   EXPECT_EQ(2u, hosts.size());
   EXPECT_EQ(GetCurrentTopHostBlacklistState(),
             optimization_guide::prefs::HintsFetcherTopHostBlacklistState::
diff --git a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc
index 2f98517..035c8f0b 100644
--- a/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/aborts_page_load_metrics_observer_unittest.cc
@@ -22,12 +22,12 @@
     page_load_metrics::mojom::PageLoadTiming timing;
     page_load_metrics::InitPageLoadTimingForTest(&timing);
     timing.navigation_start = base::Time::FromDoubleT(1);
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
   }
 
   int CountTotalAbortMetricsRecorded() {
     base::HistogramTester::CountsMap counts_map =
-        histogram_tester().GetTotalCountsForPrefix(
+        tester()->histogram_tester().GetTotalCountsForPrefix(
             "PageLoad.Experimental.AbortTiming.");
     int count = 0;
     for (const auto& entry : counts_map)
@@ -37,58 +37,58 @@
 };
 
 TEST_F(AbortsPageLoadMetricsObserverTest, NewNavigationBeforeCommit) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user performing another navigation before commit.
   NavigateAndCommit(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortNewNavigationBeforeCommit, 1);
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, ReloadBeforeCommit) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user performing another navigation before commit.
-  NavigateWithPageTransitionAndCommit(GURL("https://www.example.com"),
-                                      ui::PAGE_TRANSITION_RELOAD);
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateWithPageTransitionAndCommit(GURL("https://www.example.com"),
+                                                ui::PAGE_TRANSITION_RELOAD);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortReloadBeforeCommit, 1);
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, ForwardBackBeforeCommit) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user performing another navigation before commit.
-  NavigateWithPageTransitionAndCommit(GURL("https://www.example.com"),
-                                      ui::PAGE_TRANSITION_FORWARD_BACK);
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateWithPageTransitionAndCommit(
+      GURL("https://www.example.com"), ui::PAGE_TRANSITION_FORWARD_BACK);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortForwardBackBeforeCommit, 1);
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, BackgroundBeforeCommit) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the tab being backgrounded.
   web_contents()->WasHidden();
 
   NavigateAndCommit(GURL("about:blank"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortBackgroundBeforeCommit, 1);
   EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest,
        NewProvisionalNavigationBeforeCommit) {
-  StartNavigation(GURL("https://www.google.com"));
-  StartNavigation(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.example.com"));
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortNewNavigationBeforeCommit, 1);
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest,
        NewNavigationBeforeCommitNonTrackedPageLoad) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user performing another navigation before commit. Navigate to
   // an untracked URL, to verify that we still log abort metrics even if the new
   // navigation isn't tracked.
   NavigateAndCommit(GURL("about:blank"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortNewNavigationBeforeCommit, 1);
 }
 
@@ -97,7 +97,7 @@
   SimulateTimingWithoutPaint();
   // Simulate the user performing another navigation before paint.
   NavigateAndCommit(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortNewNavigationBeforePaint, 1);
 }
 
@@ -105,9 +105,9 @@
   NavigateAndCommit(GURL("https://www.example.com"));
   SimulateTimingWithoutPaint();
   // Simulate the user performing a reload navigation before paint.
-  NavigateWithPageTransitionAndCommit(GURL("https://www.google.com"),
-                                      ui::PAGE_TRANSITION_RELOAD);
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateWithPageTransitionAndCommit(GURL("https://www.google.com"),
+                                                ui::PAGE_TRANSITION_RELOAD);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortReloadBeforePaint, 1);
 }
 
@@ -115,11 +115,11 @@
   NavigateAndCommit(GURL("https://www.example.com"));
   SimulateTimingWithoutPaint();
   // Simulate the user performing a forward/back navigation before paint.
-  NavigateWithPageTransitionAndCommit(
+  tester()->NavigateWithPageTransitionAndCommit(
       GURL("https://www.google.com"),
       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED |
                                 ui::PAGE_TRANSITION_FORWARD_BACK));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortForwardBackBeforePaint, 1);
 }
 
@@ -129,20 +129,20 @@
   // Simulate the tab being backgrounded.
   web_contents()->WasHidden();
   NavigateAndCommit(GURL("https://www.google.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortBackgroundBeforePaint, 1);
   EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, StopBeforeCommit) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user pressing the stop button.
   web_contents()->Stop();
   // Now close the tab. This will trigger logging for the prior navigation which
   // was stopped above.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortStopBeforeCommit,
-                                      1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortStopBeforeCommit, 1);
   EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
@@ -154,8 +154,8 @@
   // Now close the tab. This will trigger logging for the prior navigation which
   // was stopped above.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortStopBeforePaint,
-                                      1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortStopBeforePaint, 1);
   EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
@@ -164,24 +164,24 @@
   NavigateAndCommit(GURL("https://www.google.com"));
   SimulateTimingWithoutPaint();
   // Now start a second navigation, but don't commit it.
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user pressing the stop button. This should cause us to record
   // two abort stop histograms, one before commit and the other before paint.
   web_contents()->Stop();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortStopBeforeCommit,
-                                      1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortStopBeforePaint,
-                                      1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortStopBeforeCommit, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortStopBeforePaint, 1);
   EXPECT_EQ(2, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, CloseBeforeCommit) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortCloseBeforeCommit, 1);
   EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
@@ -191,8 +191,8 @@
   SimulateTimingWithoutPaint();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortCloseBeforePaint,
-                                      1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortCloseBeforePaint, 1);
   EXPECT_EQ(1, CountTotalAbortMetricsRecorded());
 }
 
@@ -202,36 +202,36 @@
   NavigateAndCommit(GURL("https://www.google.com"));
   SimulateTimingWithoutPaint();
   // Now start a second navigation, but don't commit it.
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortCloseBeforeCommit, 1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortCloseBeforePaint,
-                                      1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortCloseBeforePaint, 1);
   EXPECT_EQ(2, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest,
        AbortStopBeforeCommitAndCloseBeforePaint) {
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user pressing the stop button.
   web_contents()->Stop();
   NavigateAndCommit(GURL("https://www.example.com"));
   SimulateTimingWithoutPaint();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortStopBeforeCommit,
-                                      1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramAbortCloseBeforePaint,
-                                      1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortStopBeforeCommit, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramAbortCloseBeforePaint, 1);
   EXPECT_EQ(2, CountTotalAbortMetricsRecorded());
 }
 
 TEST_F(AbortsPageLoadMetricsObserverTest, NoAbortNewNavigationFromAboutURL) {
   NavigateAndCommit(GURL("about:blank"));
   NavigateAndCommit(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortNewNavigationBeforePaint, 0);
   EXPECT_EQ(0, CountTotalAbortMetricsRecorded());
 }
@@ -243,7 +243,7 @@
   NavigateAndCommit(GURL("https://www.example.com"));
   // Since the navigation to google.com had no timing information associated
   // with it, no abort is logged.
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramAbortNewNavigationBeforePaint, 0);
   EXPECT_EQ(0, CountTotalAbortMetricsRecorded());
 }
@@ -255,7 +255,7 @@
   timing.paint_timing->first_paint = base::TimeDelta::FromMicroseconds(1);
   PopulateRequiredTimingFields(&timing);
   NavigateAndCommit(GURL("https://www.google.com"));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // The test cannot assume that abort time will be > first_paint
   // (1 micro-sec). If the system clock is low resolution, PageLoadTracker's
@@ -265,7 +265,7 @@
   NavigateAndCommit(GURL("https://www.example.com"));
 
   base::HistogramTester::CountsMap counts_map =
-      histogram_tester().GetTotalCountsForPrefix(
+      tester()->histogram_tester().GetTotalCountsForPrefix(
           internal::kHistogramAbortNewNavigationBeforePaint);
 
   EXPECT_TRUE(counts_map.empty() ||
diff --git a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
index feda6ed..65ed7e7 100644
--- a/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ad_metrics/ads_page_load_metrics_observer_unittest.cc
@@ -22,13 +22,13 @@
 #include "base/time/time.h"
 #include "chrome/browser/heavy_ad_intervention/heavy_ad_blocklist.h"
 #include "chrome/browser/page_load_metrics/observers/ad_metrics/frame_data.h"
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h"
 #include "chrome/browser/subresource_filter/subresource_filter_test_harness.h"
 #include "chrome/common/chrome_features.h"
 #include "components/blacklist/opt_out_blacklist/opt_out_blacklist_data.h"
 #include "components/blacklist/opt_out_blacklist/opt_out_blacklist_delegate.h"
 #include "components/blacklist/opt_out_blacklist/opt_out_store.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
diff --git a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc
index c07a6ba..282955f4 100644
--- a/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/amp_page_load_metrics_observer_unittest.cc
@@ -49,7 +49,7 @@
 
   void RunTest(const GURL& url) {
     NavigateAndCommit(url);
-    SimulateTimingUpdate(timing_);
+    tester()->SimulateTimingUpdate(timing_);
 
     // Navigate again to force OnComplete, which happens when a new navigation
     // occurs.
@@ -63,17 +63,18 @@
     const size_t kTypeOffset = strlen("PageLoad.Clients.AMP.");
     std::string view_type_histogram = histogram;
     view_type_histogram.insert(kTypeOffset, view_type);
-    histogram_tester().ExpectTotalCount(histogram, expect_histograms ? 1 : 0);
-    histogram_tester().ExpectTotalCount(view_type_histogram,
-                                        expect_histograms ? 1 : 0);
+    tester()->histogram_tester().ExpectTotalCount(histogram,
+                                                  expect_histograms ? 1 : 0);
+    tester()->histogram_tester().ExpectTotalCount(view_type_histogram,
+                                                  expect_histograms ? 1 : 0);
     if (!expect_histograms)
       return;
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         histogram,
         static_cast<base::HistogramBase::Sample>(
             event.value().InMilliseconds()),
         1);
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         view_type_histogram,
         static_cast<base::HistogramBase::Sample>(
             event.value().InMilliseconds()),
@@ -82,10 +83,10 @@
 
   ukm::mojom::UkmEntryPtr GetAmpPageLoadUkmEntry(const GURL& url) {
     ukm::mojom::UkmEntryPtr entry;
-    for (auto& it : test_ukm_recorder().GetMergedEntriesByName(
+    for (auto& it : tester()->test_ukm_recorder().GetMergedEntriesByName(
              ukm::builders::AmpPageLoad::kEntryName)) {
       const ukm::UkmSource* source =
-          test_ukm_recorder().GetSourceForSourceId(it.first);
+          tester()->test_ukm_recorder().GetSourceForSourceId(it.first);
       if (source->url() == url) {
         entry = std::move(it.second);
       }
@@ -106,42 +107,48 @@
 
 TEST_F(AMPPageLoadMetricsObserverTest, AMPCachePage) {
   RunTest(GURL("https://cdn.ampproject.org/page"));
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
 
 TEST_F(AMPPageLoadMetricsObserverTest, GoogleSearchAMPCachePage) {
   RunTest(GURL("https://www.google.com/amp/page"));
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
 
 TEST_F(AMPPageLoadMetricsObserverTest, GoogleSearchAMPCachePageBaseURL) {
   RunTest(GURL("https://www.google.com/amp/"));
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
 
 TEST_F(AMPPageLoadMetricsObserverTest, GoogleNewsAMPCachePage) {
   RunTest(GURL("https://news.google.com/news/amp?page"));
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
 
 TEST_F(AMPPageLoadMetricsObserverTest, GoogleNewsAMPCachePageBaseURL) {
   RunTest(GURL("https://news.google.com/news/amp"));
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
 
 TEST_F(AMPPageLoadMetricsObserverTest, NonAMPPage) {
   RunTest(GURL("https://www.google.com/not-amp/page"));
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
@@ -156,18 +163,19 @@
       ->CommitSameDocument();
 
   // Verify that subframe metrics aren't recorded without an AMP subframe.
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       0);
 
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
@@ -195,20 +203,20 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       0);
@@ -221,16 +229,18 @@
 
   // We expect a source with a negative NavigationDelta metric, since the main
   // frame navigation occurred before the AMP subframe navigation.
-  const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
-      sub_frame_entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
+  const int64_t* nav_delta_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(
+          sub_frame_entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
   EXPECT_NE(nullptr, nav_delta_metric);
   EXPECT_GE(*nav_delta_metric, 0ll);
 
-  const int64_t* amp_subframe_metric = test_ukm_recorder().GetEntryMetric(
-      main_frame_entry.get(), "SubFrameAmpPageLoad");
+  const int64_t* amp_subframe_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(main_frame_entry.get(),
+                                                   "SubFrameAmpPageLoad");
   EXPECT_NE(nullptr, amp_subframe_metric);
   EXPECT_GE(*amp_subframe_metric, 1ll);
-  EXPECT_EQ(nullptr, test_ukm_recorder().GetEntryMetric(
+  EXPECT_EQ(nullptr, tester()->test_ukm_recorder().GetEntryMetric(
                          main_frame_entry.get(), "MainFrameAmpPageLoad"));
 }
 
@@ -257,20 +267,20 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       0);
@@ -280,9 +290,10 @@
 
   // We expect a source with a positive NavigationDelta metric, since the main
   // frame navigation occurred after the AMP subframe navigation.
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
-  const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
-      entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
+  const int64_t* nav_delta_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(
+          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
   EXPECT_NE(nullptr, nav_delta_metric);
   EXPECT_LE(*nav_delta_metric, 0ll);
 }
@@ -307,7 +318,7 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   page_load_metrics::mojom::PageLoadTiming subframe_timing;
   page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
@@ -323,30 +334,30 @@
       base::TimeDelta::FromMilliseconds(3);
   PopulateRequiredTimingFields(&subframe_timing);
 
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.PaintTiming.InputToLargestContentfulPaint.Subframe",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay4.Subframe", 1);
 
   ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
   ASSERT_NE(nullptr, entry.get());
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(), "SubFrame.InteractiveTiming.FirstInputDelay4", 3);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(), "SubFrame.PaintTiming.NavigationToFirstContentfulPaint", 5);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(), "SubFrame.PaintTiming.NavigationToLargestContentfulPaint",
       10);
 }
@@ -371,26 +382,26 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   page_load_metrics::mojom::FrameRenderDataUpdate render_data(1.0, 0.5);
-  SimulateRenderDataUpdate(render_data, subframe);
+  tester()->SimulateRenderDataUpdate(render_data, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.AMP.LayoutInstability.CumulativeShiftScore.Subframe",
       10, 1);
 
   ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
   ASSERT_NE(nullptr, entry.get());
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(), "SubFrame.LayoutInstability.CumulativeShiftScore", 100);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(),
       "SubFrame.LayoutInstability.CumulativeShiftScore.BeforeInputOrScroll",
       50);
@@ -411,7 +422,7 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   page_load_metrics::mojom::PageLoadTiming subframe_timing;
   page_load_metrics::InitPageLoadTimingForTest(&subframe_timing);
@@ -427,33 +438,33 @@
       base::TimeDelta::FromMilliseconds(3);
   PopulateRequiredTimingFields(&subframe_timing);
 
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.PaintTiming.InputToFirstContentfulPaint.Subframe."
       "FullNavigation",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.PaintTiming.InputToLargestContentfulPaint.Subframe."
       "FullNavigation",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.InteractiveTiming.FirstInputDelay4.Subframe."
       "FullNavigation",
       1);
 
   ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(), "SubFrame.InteractiveTiming.FirstInputDelay4", 3);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(), "SubFrame.PaintTiming.NavigationToFirstContentfulPaint", 5);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry.get(), "SubFrame.PaintTiming.NavigationToLargestContentfulPaint",
       10);
 }
@@ -478,23 +489,24 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(GURL("https://www.example.com/"),
                                                main_rfh())
       ->Commit();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       1);
 
   // We expect a source with a negative NavigationDelta metric, since the main
   // frame navigation occurred before the AMP subframe navigation.
   ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
-  const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
-      entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
+  const int64_t* nav_delta_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(
+          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
   EXPECT_NE(nullptr, nav_delta_metric);
   EXPECT_GE(*nav_delta_metric, 0ll);
 }
@@ -519,25 +531,26 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       0);
 
   // Delete the subframe, which should trigger metrics recording.
   content::RenderFrameHostTester::For(subframe)->Detach();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       1);
 
   // We expect a source with a negative NavigationDelta metric, since the main
   // frame navigation occurred before the AMP subframe navigation.
   ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
-  const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
-      entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
+  const int64_t* nav_delta_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(
+          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
   EXPECT_NE(nullptr, nav_delta_metric);
   EXPECT_GE(*nav_delta_metric, 0ll);
 }
@@ -574,8 +587,8 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe1);
-  SimulateMetadataUpdate(metadata, subframe2);
+  tester()->SimulateMetadataUpdate(metadata, subframe1);
+  tester()->SimulateMetadataUpdate(metadata, subframe2);
 
   // Navigate the main frame to trigger metrics recording - we expect metrics to
   // have been recorded for 1 AMP page (the non-prerendered page).
@@ -583,13 +596,13 @@
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       0);
@@ -606,19 +619,19 @@
 
   // We now expect one NavigationToInput (for the prerender) and one
   // InputToNavigation (for the non-prerender).
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       0);
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> entries =
-      test_ukm_recorder().GetMergedEntriesByName(
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
           ukm::builders::AmpPageLoad::kEntryName);
   EXPECT_EQ(3ull, entries.size());
 
@@ -626,7 +639,7 @@
   const ukm::UkmSource* source2 = nullptr;
   for (const auto& kv : entries) {
     const ukm::UkmSource* candidate =
-        test_ukm_recorder().GetSourceForSourceId(kv.first);
+        tester()->test_ukm_recorder().GetSourceForSourceId(kv.first);
     ASSERT_NE(nullptr, candidate);
     if (candidate->url() == amp_url1) {
       source1 = candidate;
@@ -647,15 +660,17 @@
 
   // The entry for amp_url1 should have a negative NavigationDelta metric, since
   // the main frame navigation occurred before the AMP subframe navigation.
-  const int64_t* entry1_nav_delta_metric = test_ukm_recorder().GetEntryMetric(
-      entry1, "SubFrame.MainFrameToSubFrameNavigationDelta");
+  const int64_t* entry1_nav_delta_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(
+          entry1, "SubFrame.MainFrameToSubFrameNavigationDelta");
   EXPECT_NE(nullptr, entry1_nav_delta_metric);
   EXPECT_GE(*entry1_nav_delta_metric, 0ll);
 
   // The entry for amp_url2 should have a positive NavigationDelta metric, since
   // the main frame navigation occurred after the AMP subframe navigation.
-  const int64_t* entry2_nav_delta_metric = test_ukm_recorder().GetEntryMetric(
-      entry2, "SubFrame.MainFrameToSubFrameNavigationDelta");
+  const int64_t* entry2_nav_delta_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(
+          entry2, "SubFrame.MainFrameToSubFrameNavigationDelta");
   EXPECT_NE(nullptr, entry2_nav_delta_metric);
   EXPECT_LE(*entry2_nav_delta_metric, 0ll);
 }
@@ -677,20 +692,20 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       1);
@@ -698,9 +713,10 @@
   // We expect a source with a negative NavigationDelta metric, since the main
   // frame navigation occurred before the AMP subframe navigation.
   ukm::mojom::UkmEntryPtr entry = GetAmpPageLoadUkmEntry(amp_url);
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
-  const int64_t* nav_delta_metric = test_ukm_recorder().GetEntryMetric(
-      entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry.get(), amp_url);
+  const int64_t* nav_delta_metric =
+      tester()->test_ukm_recorder().GetEntryMetric(
+          entry.get(), "SubFrame.MainFrameToSubFrameNavigationDelta");
   EXPECT_NE(nullptr, nav_delta_metric);
   EXPECT_GE(*nav_delta_metric, 0ll);
 }
@@ -725,18 +741,19 @@
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       0);
 
-  EXPECT_TRUE(test_ukm_recorder()
+  EXPECT_TRUE(tester()
+                  ->test_ukm_recorder()
                   .GetEntriesByName(ukm::builders::AmpPageLoad::kEntryName)
                   .empty());
 }
@@ -760,20 +777,20 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags =
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorAmpDocumentLoaded;
-  SimulateMetadataUpdate(metadata, subframe);
+  tester()->SimulateMetadataUpdate(metadata, subframe);
 
   // Navigate the main frame to trigger metrics recording.
   NavigationSimulator::CreateRendererInitiated(
       GURL("https://ampviewer.com/other"), main_rfh())
       ->CommitSameDocument();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.NavigationToInput.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming.InputToNavigation.Subframe",
       0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.AMP.Experimental.PageTiming."
       "MainFrameToSubFrameNavigationDelta.Subframe",
       0);
diff --git a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc
index 1d5220d..63052d9 100644
--- a/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/android_page_load_metrics_observer_unittest.cc
@@ -200,7 +200,8 @@
       nullptr
       /* data_reduction_proxy_data */,
       content::ResourceType::kMainFrame, 0, std::move(load_timing_info));
-  SimulateLoadedResource(info, navigation_simulator->GetGlobalRequestID());
+  tester()->SimulateLoadedResource(info,
+                                   navigation_simulator->GetGlobalRequestID());
   EXPECT_EQ(kNow.since_origin().InMilliseconds(),
             observer()->reported_dns_start_ms());
 }
@@ -218,7 +219,7 @@
       base::TimeDelta::FromMilliseconds(20);
   PopulateRequiredTimingFields(&timing);
   NavigateAndCommit(GURL("https://www.example.com"));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   EXPECT_EQ(30, observer()->reported_load_event_start_ms());
   EXPECT_EQ(GetNavigationStartMicroseconds(),
             observer()->reported_navigation_start_tick_load());
diff --git a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
index 8fdda8d..f24cc96 100644
--- a/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/core_page_load_metrics_observer_unittest.cc
@@ -46,10 +46,13 @@
 };
 
 TEST_F(CorePageLoadMetricsObserverTest, NoMetrics) {
-  histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDomContentLoaded, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
+                                                0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest,
@@ -63,17 +66,20 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrlAnchor));
 
   NavigateAndCommit(GURL(kDefaultTestUrl2));
-  histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout, 1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstLayout,
-                                       first_layout.InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDomContentLoaded, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
+                                                1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstLayout, first_layout.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, SingleMetricAfterCommit) {
@@ -98,28 +104,31 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout, 1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstLayout,
-                                       first_layout.InMilliseconds(), 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDomContentLoaded, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
+                                                1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstLayout, first_layout.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramParseDuration,
       (parse_stop - parse_start).InMilliseconds(), 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramParseBlockedOnScriptLoad,
       parse_script_load_duration.InMilliseconds(), 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramParseBlockedOnScriptExecution,
       parse_script_exec_duration.InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 0);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramPageTimingForegroundDuration, 1);
 }
 
@@ -144,13 +153,13 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
-                                      1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstContentfulPaint,
-                                       first_contentful_paint.InMilliseconds(),
-                                       1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstContentfulPaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstContentfulPaint,
+      first_contentful_paint.InMilliseconds(), 1);
 
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
@@ -160,32 +169,36 @@
   timing2.document_timing->first_layout = first_layout_2;
   PopulateRequiredTimingFields(&timing2);
 
-  SimulateTimingUpdate(timing2);
+  tester()->SimulateTimingUpdate(timing2);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout, 2);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstLayout,
-                                       first_layout_1.InMilliseconds(), 1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstLayout,
-                                       first_layout_2.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
+                                                2);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstLayout, first_layout_1.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstLayout, first_layout_2.InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstContentfulPaint,
-                                      1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstContentfulPaint,
-                                       first_contentful_paint.InMilliseconds(),
-                                       1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstImagePaint,
-                                       first_image_paint.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstContentfulPaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstContentfulPaint,
+      first_contentful_paint.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstImagePaint, first_image_paint.InMilliseconds(),
+      1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramDomContentLoaded,
-                                       dom_content.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDomContentLoaded, 1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramDomContentLoaded, dom_content.InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramLoad,
-                                       load.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 1);
+  tester()->histogram_tester().ExpectBucketCount(internal::kHistogramLoad,
+                                                 load.InMilliseconds(), 1);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, BackgroundDifferentHistogram) {
@@ -200,7 +213,7 @@
   // Simulate "Open link in new tab."
   web_contents()->WasHidden();
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate switching to the tab and making another navigation.
   web_contents()->WasShown();
@@ -208,21 +221,25 @@
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kBackgroundHistogramDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kBackgroundHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kBackgroundHistogramFirstLayout,
-                                      1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kBackgroundHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kBackgroundHistogramFirstLayout, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kBackgroundHistogramFirstLayout, first_layout.InMilliseconds(),
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kBackgroundHistogramFirstImagePaint, 0);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDomContentLoaded, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
+                                                0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, OnlyBackgroundLaterEvents) {
@@ -239,14 +256,14 @@
   ASSERT_FALSE(timing.paint_timing->first_image_paint);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Background the tab, then foreground it.
   web_contents()->WasHidden();
   web_contents()->WasShown();
   timing.paint_timing->first_image_paint = base::TimeDelta::FromSeconds(4);
   PopulateRequiredTimingFields(&timing);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
@@ -256,34 +273,36 @@
   // dom_content_loaded_event_start.
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.document_timing->dom_content_loaded_event_start,
-          GetDelegateForCommittedLoad())) {
-    histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded,
-                                        1);
-    histogram_tester().ExpectBucketCount(
+          tester()->GetDelegateForCommittedLoad())) {
+    tester()->histogram_tester().ExpectTotalCount(
+        internal::kHistogramDomContentLoaded, 1);
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kHistogramDomContentLoaded,
         timing.document_timing->dom_content_loaded_event_start.value()
             .InMilliseconds(),
         1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kBackgroundHistogramDomContentLoaded, 0);
   } else {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kBackgroundHistogramDomContentLoaded, 1);
-    histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded,
-                                        0);
+    tester()->histogram_tester().ExpectTotalCount(
+        internal::kHistogramDomContentLoaded, 0);
   }
 
-  histogram_tester().ExpectTotalCount(internal::kBackgroundHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kBackgroundHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kBackgroundHistogramFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kBackgroundHistogramFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 0);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramPageTimingForegroundDuration, 1);
 }
 
@@ -301,24 +320,27 @@
   web_contents()->WasHidden();
 
   // Open in new tab
-  StartNavigation(GURL(kDefaultTestUrl));
+  tester()->StartNavigation(GURL(kDefaultTestUrl));
 
   // Switch to the tab
   web_contents()->WasShown();
 
   // Start another provisional load
   NavigateAndCommit(GURL(kDefaultTestUrl2));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to see if the timing updated for the foregrounded load.
   NavigateAndCommit(GURL(kDefaultTestUrl));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout, 1);
-  histogram_tester().ExpectBucketCount(internal::kHistogramFirstLayout,
-                                       first_layout.InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDomContentLoaded, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
+                                                1);
+  tester()->histogram_tester().ExpectBucketCount(
+      internal::kHistogramFirstLayout, first_layout.InMilliseconds(), 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, FailedProvisionalLoad) {
@@ -330,16 +352,19 @@
   navigation->Fail(net::ERR_TIMED_OUT);
   navigation->AbortCommit();
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstImagePaint, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFailedProvisionalLoad,
-                                      1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDomContentLoaded, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFirstLayout,
+                                                0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFailedProvisionalLoad, 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramPageTimingForegroundDuration, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramPageTimingForegroundDurationNoCommit, 1);
 }
 
@@ -351,8 +376,8 @@
   content::NavigationSimulator::NavigateAndFailFromDocument(
       url, net::ERR_TIMED_OUT, main_rfh());
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFailedProvisionalLoad,
-                                      0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFailedProvisionalLoad, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, Reload) {
@@ -365,12 +390,13 @@
   PopulateRequiredTimingFields(&timing);
 
   GURL url(kDefaultTestUrl);
-  NavigateWithPageTransitionAndCommit(url, ui::PAGE_TRANSITION_RELOAD);
-  SimulateTimingUpdate(timing);
+  tester()->NavigateWithPageTransitionAndCommit(url,
+                                                ui::PAGE_TRANSITION_RELOAD);
+  tester()->SimulateTimingUpdate(timing);
 
   auto resources =
       GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
   int64_t network_bytes = 0;
   int64_t cache_bytes = 0;
   for (const auto& resource : resources) {
@@ -383,49 +409,49 @@
     }
   }
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintReload, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramLoadTypeFirstContentfulPaintReload,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartReload, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramLoadTypeParseStartReload,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartNewNavigation, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeNetworkBytesReload,
       static_cast<int>((network_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeNetworkBytesForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeNetworkBytesNewNavigation, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeCacheBytesReload,
       static_cast<int>((cache_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeCacheBytesForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeCacheBytesNewNavigation, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeTotalBytesReload,
       static_cast<int>((network_bytes + cache_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeTotalBytesForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeTotalBytesNewNavigation, 0);
 }
 
@@ -443,14 +469,14 @@
   // of PAGE_TRANSITION_RELOAD with a PAGE_TRANSITION_FORWARD_BACK
   // modifier. This test verifies that when we encounter such a page, we log it
   // as a forward/back navigation.
-  NavigateWithPageTransitionAndCommit(
+  tester()->NavigateWithPageTransitionAndCommit(
       url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_RELOAD |
                                      ui::PAGE_TRANSITION_FORWARD_BACK));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   auto resources =
       GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
   int64_t network_bytes = 0;
   int64_t cache_bytes = 0;
   for (const auto& resource : resources) {
@@ -463,49 +489,49 @@
     }
   }
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintReload, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintForwardBack, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramLoadTypeFirstContentfulPaintForwardBack,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartReload, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartForwardBack, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramLoadTypeParseStartForwardBack,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartNewNavigation, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeNetworkBytesForwardBack,
       static_cast<int>((network_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeNetworkBytesNewNavigation, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeNetworkBytesReload, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeCacheBytesForwardBack,
       static_cast<int>((cache_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeCacheBytesNewNavigation, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeCacheBytesReload, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeTotalBytesForwardBack,
       static_cast<int>((network_bytes + cache_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeTotalBytesNewNavigation, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeTotalBytesReload, 0);
 }
 
@@ -519,12 +545,12 @@
   PopulateRequiredTimingFields(&timing);
 
   GURL url(kDefaultTestUrl);
-  NavigateWithPageTransitionAndCommit(url, ui::PAGE_TRANSITION_LINK);
-  SimulateTimingUpdate(timing);
+  tester()->NavigateWithPageTransitionAndCommit(url, ui::PAGE_TRANSITION_LINK);
+  tester()->SimulateTimingUpdate(timing);
 
   auto resources =
       GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
   int64_t network_bytes = 0;
   int64_t cache_bytes = 0;
   for (const auto& resource : resources) {
@@ -537,68 +563,68 @@
     }
   }
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintReload, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramLoadTypeFirstContentfulPaintNewNavigation,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartReload, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeParseStartNewNavigation, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramLoadTypeParseStartNewNavigation,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeNetworkBytesNewNavigation,
       static_cast<int>((network_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeNetworkBytesForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeNetworkBytesReload, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeCacheBytesNewNavigation,
       static_cast<int>((cache_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeCacheBytesForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeCacheBytesReload, 0);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramLoadTypeTotalBytesNewNavigation,
       static_cast<int>((network_bytes + cache_bytes) / 1024), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeTotalBytesForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLoadTypeTotalBytesReload, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, BytesAndResourcesCounted) {
   NavigateAndCommit(GURL(kDefaultTestUrl));
   NavigateAndCommit(GURL(kDefaultTestUrl2));
-  histogram_tester().ExpectTotalCount(internal::kHistogramPageLoadTotalBytes,
-                                      1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramPageLoadNetworkBytes,
-                                      1);
-  histogram_tester().ExpectTotalCount(internal::kHistogramPageLoadCacheBytes,
-                                      1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramPageLoadTotalBytes, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramPageLoadNetworkBytes, 1);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramPageLoadCacheBytes, 1);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramPageLoadNetworkBytesIncludingHeaders, 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramTotalCompletedResources, 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNetworkCompletedResources, 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCacheCompletedResources, 1);
 }
 
@@ -612,14 +638,14 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstMeaningfulPaint,
-                                      1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstMeaningfulPaint, 1);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramParseStartToFirstMeaningfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFirstMeaningfulPaintStatus,
       internal::FIRST_MEANINGFUL_PAINT_RECORDED, 1);
 }
@@ -635,13 +661,13 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLargestImagePaint),
-      testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestImagePaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
 }
 
 TEST_F(CorePageLoadMetricsObserverTest,
@@ -672,27 +698,29 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate again to force histogram recording in the main frame.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
   EXPECT_TRUE(
-      histogram_tester()
+      tester()
+          ->histogram_tester()
           .GetAllSamples(internal::kHistogramLargestContentfulPaintMainFrame)
           .empty());
   EXPECT_TRUE(
-      histogram_tester()
+      tester()
+          ->histogram_tester()
           .GetAllSamples(
               internal::kHistogramLargestContentfulPaintMainFrameContentType)
           .empty());
@@ -726,26 +754,26 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate again to force histogram recording in the main frame.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaintMainFrame),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintMainFrameContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
@@ -787,27 +815,27 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate again to force histogram recording in the main frame.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
           1)));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaintMainFrame),
               testing::ElementsAre(base::Bucket(9382, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintMainFrameContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
@@ -848,27 +876,27 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate again to force histogram recording in the main frame.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
           1)));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaintMainFrame),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintMainFrameContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
@@ -905,23 +933,23 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   subframe_timing.paint_timing->largest_image_paint =
       base::TimeDelta::FromMilliseconds(300);
   subframe_timing.paint_timing->largest_image_paint_size = 10u;
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate again to force histogram recording in the main frame.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
   // Ensure that the largest_image_paint timing for the main frame is recorded.
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
@@ -959,23 +987,23 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   subframe_timing.paint_timing->largest_image_paint =
       base::TimeDelta::FromMilliseconds(990);
   subframe_timing.paint_timing->largest_image_paint_size = 50u;
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Navigate again to force histogram recording in the main frame.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
   // Ensure that the largest_image_paint timing for the main frame is recorded.
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(990, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
@@ -992,11 +1020,12 @@
   web_contents()->WasHidden();
   // This event happens after first background, so it will be discarded.
   timing.paint_timing->largest_image_paint = base::Time::Now() - base::Time();
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramLargestImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestImagePaint, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, LargestImagePaint_ReportLastCandidate) {
@@ -1010,19 +1039,19 @@
       base::TimeDelta::FromMilliseconds(1000);
   timing.paint_timing->largest_image_paint_size = 10u;
   PopulateRequiredTimingFields(&timing);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   timing.paint_timing->largest_image_paint =
       base::TimeDelta::FromMilliseconds(4780);
   timing.paint_timing->largest_image_paint_size = 5u;
   PopulateRequiredTimingFields(&timing);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLargestImagePaint),
-      testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestImagePaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, ReportLastNullCandidate) {
@@ -1037,16 +1066,17 @@
   timing.paint_timing->largest_image_paint_size = 10u;
 
   PopulateRequiredTimingFields(&timing);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   timing.paint_timing->largest_image_paint = base::Optional<base::TimeDelta>();
   timing.paint_timing->largest_image_paint_size = 0;
   PopulateRequiredTimingFields(&timing);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramLargestImagePaint, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramLargestImagePaint, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, LargestTextPaint) {
@@ -1060,13 +1090,13 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLargestTextPaint),
-      testing::ElementsAre(base::Bucket(4780, 1)));
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramLargestTextPaint),
+              testing::ElementsAre(base::Bucket(4780, 1)));
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, LargestContentfulPaint_NoTextOrImage) {
@@ -1079,17 +1109,17 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLargestContentfulPaint, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLargestContentfulPaintContentType, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLargestContentfulPaintMainFrame, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramLargestContentfulPaintMainFrameContentType, 0);
 }
 
@@ -1104,15 +1134,15 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
@@ -1130,15 +1160,15 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
@@ -1160,15 +1190,15 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(4780, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kImage),
@@ -1190,15 +1220,15 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLargestContentfulPaint),
               testing::ElementsAre(base::Bucket(990, 1)));
   EXPECT_THAT(
-      histogram_tester().GetAllSamples(
+      tester()->histogram_tester().GetAllSamples(
           internal::kHistogramLargestContentfulPaintContentType),
       testing::ElementsAre(base::Bucket(
           static_cast<base::HistogramBase::Sample>(LargestContentType::kText),
@@ -1218,12 +1248,12 @@
 
   // First Meaningful Paint happens after tab is foregrounded.
   web_contents()->WasShown();
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramForegroundToFirstMeaningfulPaint, 1);
 }
 
@@ -1238,11 +1268,12 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramTimeToInteractive, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramTimeToInteractive, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramTimeToInteractiveStatus,
       internal::TIME_TO_INTERACTIVE_RECORDED, 1);
 }
@@ -1263,13 +1294,14 @@
   web_contents()->WasHidden();
   web_contents()->WasShown();
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramTimeToInteractive, 0);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramTimeToInteractive, 0);
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramTimeToInteractiveStatus,
       internal::TIME_TO_INTERACTIVE_BACKGROUNDED, 1);
 }
@@ -1290,12 +1322,13 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramTimeToInteractive, 0);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramTimeToInteractive, 0);
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramTimeToInteractiveStatus,
       internal::TIME_TO_INTERACTIVE_USER_INTERACTION_BEFORE_INTERACTIVE, 1);
 }
@@ -1310,12 +1343,13 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramTimeToInteractive, 0);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramTimeToInteractive, 0);
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramTimeToInteractiveStatus,
       internal::TIME_TO_INTERACTIVE_DID_NOT_REACH_QUIESCENCE, 1);
 }
@@ -1328,12 +1362,13 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramTimeToInteractive, 0);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramTimeToInteractive, 0);
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramTimeToInteractiveStatus,
       internal::TIME_TO_INTERACTIVE_DID_NOT_REACH_FIRST_MEANINGFUL_PAINT, 1);
 }
@@ -1350,14 +1385,14 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramFirstInputDelay4),
-      testing::ElementsAre(base::Bucket(5, 1)));
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramFirstInputDelay4),
+              testing::ElementsAre(base::Bucket(5, 1)));
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramFirstInputTimestamp4),
               testing::ElementsAre(base::Bucket(4780, 1)));
 }
@@ -1374,14 +1409,14 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  EXPECT_THAT(
-      histogram_tester().GetAllSamples(internal::kHistogramLongestInputDelay),
-      testing::ElementsAre(base::Bucket(5, 1)));
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
+                  internal::kHistogramLongestInputDelay),
+              testing::ElementsAre(base::Bucket(5, 1)));
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   internal::kHistogramLongestInputTimestamp),
               testing::ElementsAre(base::Bucket(4780, 1)));
 }
@@ -1403,13 +1438,14 @@
   web_contents()->WasHidden();
   web_contents()->WasShown();
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstInputDelay, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramFirstInputTimestamp,
-                                      0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstInputDelay, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFirstInputTimestamp, 0);
 }
 
 TEST_F(CorePageLoadMetricsObserverTest, NavigationToBackNavigationWithGesture) {
@@ -1422,10 +1458,10 @@
   simulator->Commit();
 
   // Now the user presses the back button.
-  NavigateWithPageTransitionAndCommit(
+  tester()->NavigateWithPageTransitionAndCommit(
       url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramUserGestureNavigationToForwardBack, 1);
 }
 
@@ -1440,10 +1476,10 @@
   simulator->Commit();
 
   // Now the user presses the back button.
-  NavigateWithPageTransitionAndCommit(
+  tester()->NavigateWithPageTransitionAndCommit(
       url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramUserGestureNavigationToForwardBack, 0);
 }
 
@@ -1458,10 +1494,10 @@
   simulator->Commit();
 
   // Now the user presses the back button.
-  NavigateWithPageTransitionAndCommit(
+  tester()->NavigateWithPageTransitionAndCommit(
       url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramUserGestureNavigationToForwardBack, 0);
 }
 
@@ -1476,10 +1512,10 @@
   simulator->Start();
 
   // Now the user presses the back button before the first navigation committed.
-  NavigateWithPageTransitionAndCommit(
+  tester()->NavigateWithPageTransitionAndCommit(
       url, ui::PageTransitionFromInt(ui::PAGE_TRANSITION_FORWARD_BACK));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramUserGestureNavigationToForwardBack, 1);
 }
 
@@ -1491,12 +1527,12 @@
   resources.push_back(
       CreateResource(false /* was_cached */, 10 * 1024 /* delta_bytes */,
                      0 /* encoded_body_length */, false /* is_complete */));
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
   // Navigate again to force histogram recording.
   NavigateAndCommit(GURL(kDefaultTestUrl2));
 
   // Verify that the unfinished resource bytes are recorded.
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       internal::kHistogramPageLoadUnfinishedBytes, 10, 1);
 }
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
index 361a88d..db0d91c 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_base_unittest.cc
@@ -105,7 +105,8 @@
                    uint64_t want_original_bytes,
                    uint64_t want_uuid) {
     using UkmEntry = ukm::builders::DataReductionProxy;
-    auto entries = test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName);
+    auto entries =
+        tester()->test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName);
 
     if (!want_ukm) {
       EXPECT_EQ(0u, entries.size());
@@ -114,12 +115,13 @@
 
     EXPECT_EQ(1u, entries.size());
     for (const auto* const entry : entries) {
-      test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kDefaultTestUrl));
+      tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
+          entry, GURL(kDefaultTestUrl));
 
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::kEstimatedOriginalNetworkBytesName,
           want_original_bytes);
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::kDataSaverPageUUIDName, want_uuid);
     }
   }
@@ -163,9 +165,9 @@
       false /* was_cached */, 2 * 1024 /* delta_bytes */,
       true /* is_complete */, true /* proxy_used*/, 5 /* compression_ratio */));
 
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
   // Use https://play.golang.org/p/XnMDcTiPzt8 to generate an updated uuid.
   ValidateUKM(true, 9918U, 5261012271403106530);
 }
@@ -185,9 +187,9 @@
       false /* was_cached */, 2 * 1024 /* delta_bytes */,
       true /* is_complete */, true /* proxy_used*/, 5 /* compression_ratio */));
 
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
   // Use https://play.golang.org/p/XnMDcTiPzt8 to generate an updated uuid.
   ValidateUKM(true, 9918U, 7538012597823726222);
 }
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_test_utils.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_test_utils.cc
index 1c7102b..40a59e9 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_test_utils.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_test_utils.cc
@@ -83,7 +83,7 @@
   opt_out_expected_ = opt_out_expected;
   black_listed_ = black_listed;
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing_);
+  tester()->SimulateTimingUpdate(timing_);
 }
 
 void DataReductionProxyMetricsObserverTestBase::
@@ -92,7 +92,7 @@
                                      bool opt_out_expected) {
   RunTest(data_reduction_proxy_used, is_using_lite_page, opt_out_expected,
           false);
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 }
 
 void DataReductionProxyMetricsObserverTestBase::RunLitePageRedirectTest(
@@ -101,7 +101,7 @@
   preview_info_ = preview_info;
   ect_ = ect;
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing_);
+  tester()->SimulateTimingUpdate(timing_);
 }
 
 // Verify that, if expected and actual are set, their values are equal.
diff --git a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_unittest.cc
index 77286c82b..c51f60a 100644
--- a/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/data_reduction_proxy_metrics_observer_unittest.cc
@@ -108,18 +108,18 @@
   void ValidateHistogramsForSuffix(
       const std::string& histogram_suffix,
       const base::Optional<base::TimeDelta>& event) {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(histogram_suffix),
         data_reduction_proxy_used() || cached_data_reduction_proxy_used() ? 1
                                                                           : 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         std::string(internal::kHistogramDataReductionProxyLitePagePrefix)
             .append(histogram_suffix),
         is_using_lite_page() ? 1 : 0);
     if (!(data_reduction_proxy_used() || cached_data_reduction_proxy_used()))
       return;
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(histogram_suffix),
         static_cast<base::HistogramBase::Sample>(
@@ -127,7 +127,7 @@
         1);
     if (!is_using_lite_page())
       return;
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyLitePagePrefix)
             .append(histogram_suffix),
         event.value().InMilliseconds(), is_using_lite_page() ? 1 : 0);
@@ -138,67 +138,67 @@
                               int64_t network_bytes,
                               int64_t drp_bytes,
                               int64_t ocl_bytes) {
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kResourcesPercentProxied),
         100 * drp_resources / network_resources, 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kBytesPercentProxied),
         static_cast<int>(100 * drp_bytes / network_bytes), 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kNetworkResources),
         network_resources, 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kResourcesProxied),
         drp_resources, 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kResourcesNotProxied),
         network_resources - drp_resources, 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kNetworkBytes),
         static_cast<int>(network_bytes / 1024), 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kBytesProxied),
         static_cast<int>(drp_bytes / 1024), 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kBytesNotProxied),
         static_cast<int>((network_bytes - drp_bytes) / 1024), 1);
 
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         std::string(internal::kHistogramDataReductionProxyPrefix)
             .append(internal::kBytesOriginal),
         static_cast<int>(ocl_bytes / 1024), 1);
     if (ocl_bytes < network_bytes) {
-      histogram_tester().ExpectUniqueSample(
+      tester()->histogram_tester().ExpectUniqueSample(
           std::string(internal::kHistogramDataReductionProxyPrefix)
               .append(internal::kBytesInflationPercent),
           static_cast<int>(100 * network_bytes / ocl_bytes - 100), 1);
 
-      histogram_tester().ExpectUniqueSample(
+      tester()->histogram_tester().ExpectUniqueSample(
           std::string(internal::kHistogramDataReductionProxyPrefix)
               .append(internal::kBytesInflation),
           static_cast<int>((network_bytes - ocl_bytes) / 1024), 1);
     } else {
-      histogram_tester().ExpectUniqueSample(
+      tester()->histogram_tester().ExpectUniqueSample(
           std::string(internal::kHistogramDataReductionProxyPrefix)
               .append(internal::kBytesCompressionRatio),
           static_cast<int>(100 * network_bytes / ocl_bytes), 1);
 
-      histogram_tester().ExpectUniqueSample(
+      tester()->histogram_tester().ExpectUniqueSample(
           std::string(internal::kHistogramDataReductionProxyPrefix)
               .append(internal::kBytesSavings),
           static_cast<int>((ocl_bytes - network_bytes) / 1024), 1);
@@ -266,7 +266,7 @@
       false /* is_complete */, true /* proxy_used*/,
       0.5 /* compression_ratio */));
 
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
   int network_resources = 0;
   int drp_resources = 0;
@@ -300,7 +300,7 @@
         ++drp_resources;
     }
   }
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateDataHistograms(network_resources, drp_resources,
                          insecure_network_bytes + secure_network_bytes,
@@ -331,7 +331,7 @@
       false /* is_complete */, true /* proxy_used*/,
       10 /* compression_ratio */));
 
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
   int network_resources = 0;
   int drp_resources = 0;
@@ -369,7 +369,7 @@
         ++drp_resources;
     }
   }
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateDataHistograms(network_resources, drp_resources,
                          insecure_network_bytes + secure_network_bytes,
diff --git a/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer_unittest.cc
index 69b8108..c2dc1200 100644
--- a/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/document_write_page_load_metrics_observer_unittest.cc
@@ -22,14 +22,14 @@
   }
 
   void AssertNoBlockHistogramsLogged() {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 0);
   }
 };
 
 TEST_F(DocumentWritePageLoadMetricsObserverTest, NoMetrics) {
   AssertNoBlockHistogramsLogged();
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(DocumentWritePageLoadMetricsObserverTest, PossibleBlock) {
@@ -56,28 +56,28 @@
   metadata.behavior_flags |=
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorDocumentWriteBlock;
   NavigateAndCommit(GURL("https://www.google.com/"));
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
-                                      1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDocWriteBlockCount, 1);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint,
       contentful_paint.InMilliseconds(), 1);
 
   using Entry = ukm::builders::Intervention_DocumentWrite_ScriptBlock;
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> entries =
-      test_ukm_recorder().GetMergedEntriesByName(Entry::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(Entry::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto& kv : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
         kv.second.get(), GURL("https://www.google.com/"));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         Entry::kParseTiming_ParseBlockedOnScriptLoadFromDocumentWriteName, 5);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         Entry::kParseTiming_ParseBlockedOnScriptExecutionFromDocumentWriteName,
         3);
@@ -85,9 +85,9 @@
 
   NavigateAndCommit(GURL("https://www.example.com/"));
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramDocWriteBlockParseStartToFirstContentfulPaint,
       contentful_paint.InMilliseconds(), 1);
 }
@@ -105,49 +105,51 @@
   metadata.behavior_flags |= blink::WebLoadingBehaviorFlag::
       kWebLoadingBehaviorDocumentWriteBlockReload;
   NavigateAndCommit(GURL("https://www.google.com/"));
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramDocWriteBlockReloadCount, 1);
 
   using Entry = ukm::builders::Intervention_DocumentWrite_ScriptBlock;
-  auto entries = test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
+  auto entries =
+      tester()->test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
   EXPECT_EQ(1u, entries.size());
   const ukm::mojom::UkmEntry* entry1 = nullptr;
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
         entry, GURL("https://www.google.com/"));
-    test_ukm_recorder().ExpectEntryMetric(entry, Entry::kDisabled_ReloadName,
-                                          true);
+    tester()->test_ukm_recorder().ExpectEntryMetric(
+        entry, Entry::kDisabled_ReloadName, true);
     entry1 = entry;
   }
 
   // Another reload.
   NavigateAndCommit(GURL("https://www.example.com/"));
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramDocWriteBlockReloadCount, 2);
 
-  auto entries2 = test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
+  auto entries2 =
+      tester()->test_ukm_recorder().GetEntriesByName(Entry::kEntryName);
   EXPECT_EQ(2u, entries2.size());
   for (const auto* const entry : entries2) {
     if (entry != entry1)
-      test_ukm_recorder().ExpectEntrySourceHasUrl(
+      tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
           entry, GURL("https://www.example.com/"));
-    test_ukm_recorder().ExpectEntryMetric(entry, Entry::kDisabled_ReloadName,
-                                          true);
+    tester()->test_ukm_recorder().ExpectEntryMetric(
+        entry, Entry::kDisabled_ReloadName, true);
   }
 
   // Another metadata update should not increase reload count.
   metadata.behavior_flags |=
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorServiceWorkerControlled;
-  SimulateTimingAndMetadataUpdate(timing, metadata);
-  histogram_tester().ExpectTotalCount(
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramDocWriteBlockReloadCount, 2);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramDocWriteBlockCount,
-                                      0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramDocWriteBlockCount, 0);
 }
 
 TEST_F(DocumentWritePageLoadMetricsObserverTest, NoPossibleBlock) {
@@ -160,7 +162,7 @@
 
   page_load_metrics::mojom::PageLoadMetadata metadata;
   NavigateAndCommit(GURL("https://www.google.com/"));
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
   NavigateAndCommit(GURL("https://www.example.com/"));
   AssertNoBlockHistogramsLogged();
diff --git a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc
index 08514ec..e50a27cc 100644
--- a/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/from_gws_page_load_metrics_observer_unittest.cc
@@ -35,7 +35,7 @@
     page_load_metrics::mojom::PageLoadTiming timing;
     page_load_metrics::InitPageLoadTimingForTest(&timing);
     timing.navigation_start = base::Time::FromDoubleT(1);
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
   }
 
   void SimulateTimingWithFirstPaint() {
@@ -44,7 +44,7 @@
     timing.navigation_start = base::Time::FromDoubleT(1);
     timing.paint_timing->first_paint = base::TimeDelta::FromMilliseconds(0);
     PopulateRequiredTimingFields(&timing);
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
   }
 
   void SimulateMouseEvent() {
@@ -54,16 +54,16 @@
     mouse_event.button = blink::WebMouseEvent::Button::kLeft;
     mouse_event.SetPositionInWidget(7, 7);
     mouse_event.click_count = 1;
-    SimulateInputEvent(mouse_event);
+    tester()->SimulateInputEvent(mouse_event);
   }
 };
 
 class FromGWSPageLoadMetricsLoggerTest : public testing::Test {};
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, NoMetrics) {
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, NoPreviousCommittedUrl) {
@@ -74,13 +74,13 @@
   PopulateRequiredTimingFields(&timing);
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, NonSearchPreviousCommittedUrl) {
@@ -92,13 +92,13 @@
   NavigateAndCommit(GURL("http://www.other.com"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest,
@@ -111,13 +111,13 @@
   NavigateAndCommit(GURL("https://www.google.com/"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest,
@@ -132,13 +132,13 @@
   NavigateAndCommit(GURL("https://www.google.com/search?a=b&c=d"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, SearchPreviousCommittedUrl1) {
@@ -164,75 +164,79 @@
   NavigateAndCommit(GURL("https://www.google.com/webhp?q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFromGWSParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFromGWSParseStart, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFromGWSFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFromGWSFirstPaint, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFromGWSParseDuration,
-                                      1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramFromGWSParseDuration, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSParseDuration,
       (timing.parse_timing->parse_stop.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramFromGWSLoad, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(internal::kHistogramFromGWSLoad,
+                                                1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstInputDelay, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstInputDelay,
       timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -245,21 +249,22 @@
   NavigateAndCommit(GURL("https://www.google.com/#q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -272,21 +277,22 @@
   NavigateAndCommit(GURL("https://www.google.com/webhp#q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -299,21 +305,22 @@
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -331,24 +338,25 @@
   PopulateRequiredTimingFields(&timing2);
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   NavigateAndCommit(GURL("http://www.example.com/other"));
-  SimulateTimingUpdate(timing2);
+  tester()->SimulateTimingUpdate(timing2);
 
   // Navigate again to force logging. We expect to log timing for the page
   // navigated from search, but not for the page navigated from that page.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -366,24 +374,25 @@
   PopulateRequiredTimingFields(&timing2);
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
-  SimulateTimingUpdate(timing2);
+  tester()->SimulateTimingUpdate(timing2);
 
   // Navigate again to force logging. We expect to log timing for the page
   // navigated from search, but not for the search page we navigated to.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -408,29 +417,30 @@
   PopulateRequiredTimingFields(&timing3);
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
-  SimulateTimingUpdate(timing2);
+  tester()->SimulateTimingUpdate(timing2);
   NavigateAndCommit(GURL(kExampleUrl));
-  SimulateTimingUpdate(timing3);
+  tester()->SimulateTimingUpdate(timing3);
 
   // Navigate again to force logging. We expect to log timing for both pages
   // navigated from search, but not for the search pages we navigated to.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 2);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing3.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(2u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -455,27 +465,28 @@
   PopulateRequiredTimingFields(&timing3);
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
   NavigateAndCommit(GURL(kExampleUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
   NavigateAndCommit(GURL("https://www.google.co.uk/search#q=test"));
   web_contents()->WasHidden();
-  SimulateTimingUpdate(timing2);
+  tester()->SimulateTimingUpdate(timing2);
   NavigateAndCommit(GURL(kExampleUrl));
-  SimulateTimingUpdate(timing3);
+  tester()->SimulateTimingUpdate(timing3);
 
   // Navigate again to force logging. We expect to log timing for the first page
   // navigated from search, but not the second since it was backgrounded.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(2u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -490,21 +501,22 @@
   NavigateAndCommit(GURL("https://www.google.com/url?source=web"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramFromGWSFirstImagePaint,
       timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
 
-  auto entries = test_ukm_recorder().GetEntriesByName(
+  auto entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_FromGoogleSearch::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* const entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kExampleUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                          GURL(kExampleUrl));
   }
 }
 
@@ -519,14 +531,14 @@
   NavigateAndCommit(GURL("https://www.google.com/url?a=b&c=d"));
   NavigateAndCommit(GURL(kExampleUrl));
 
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Navigate again to force logging.
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSFirstImagePaint, 0);
 
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest,
@@ -541,31 +553,31 @@
   NavigateAndCommit(GURL(kExampleUrl));
 
   web_contents()->WasHidden();
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // If the system clock is low resolution PageLoadTracker's background_time_
   // may be < timing.first_image_paint.
   if (page_load_metrics::WasStartedInForegroundOptionalEventInForeground(
           timing.paint_timing->first_image_paint,
-          GetDelegateForCommittedLoad())) {
-    histogram_tester().ExpectTotalCount(
+          tester()->GetDelegateForCommittedLoad())) {
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramFromGWSFirstImagePaint, 1);
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kHistogramFromGWSFirstImagePaint,
         timing.paint_timing->first_image_paint.value().InMilliseconds(), 1);
   } else {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramFromGWSFirstImagePaint, 0);
   }
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, NewNavigationBeforeCommit) {
   NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
-  StartNavigation(GURL("http://example.test"));
+  tester()->StartNavigation(GURL("http://example.test"));
 
   // Simulate the user performing another navigation before commit.
   NavigateAndCommit(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortNewNavigationBeforeCommit, 1);
 }
 
@@ -575,43 +587,43 @@
   SimulateTimingWithoutPaint();
   // Simulate the user performing another navigation before paint.
   NavigateAndCommit(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortNewNavigationBeforePaint, 1);
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, StopBeforeCommit) {
   NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
-  StartNavigation(GURL("http://example.test"));
+  tester()->StartNavigation(GURL("http://example.test"));
   // Simulate the user pressing the stop button.
   web_contents()->Stop();
   // Now close the tab. This will trigger logging for the prior navigation which
   // was stopped above.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforeCommit, 1);
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, StopBeforeCommitNonSearch) {
   NavigateAndCommit(GURL("http://google.com"));
-  StartNavigation(GURL("http://example.test"));
+  tester()->StartNavigation(GURL("http://example.test"));
   // Simulate the user pressing the stop button.
   web_contents()->Stop();
   // Now close the tab. This will trigger logging for the prior navigation which
   // was stopped above.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforeCommit, 0);
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, StopBeforeCommitSearchToSearch) {
   NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
-  StartNavigation(GURL("http://www.google.com/webhp?q=5"));
+  tester()->StartNavigation(GURL("http://www.google.com/webhp?q=5"));
   // Simulate the user pressing the stop button.
   web_contents()->Stop();
   // Now close the tab. This will trigger logging for the prior navigation which
   // was stopped above.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforeCommit, 0);
 }
 
@@ -624,7 +636,7 @@
   // Now close the tab. This will trigger logging for the prior navigation which
   // was stopped above.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforePaint, 1);
 }
 
@@ -634,25 +646,25 @@
   NavigateAndCommit(GURL("https://example.test"));
   SimulateTimingWithoutPaint();
   // Now start a second navigation, but don't commit it.
-  StartNavigation(GURL("https://www.google.com"));
+  tester()->StartNavigation(GURL("https://www.google.com"));
   // Simulate the user pressing the stop button. This should cause us to record
   // stop metrics for the FromGWS committed load, too.
   web_contents()->Stop();
   // Simulate closing the tab.
   DeleteContents();
   // The second navigation was not from GWS.
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforeCommit, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforePaint, 1);
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, CloseBeforeCommit) {
   NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
-  StartNavigation(GURL("https://example.test"));
+  tester()->StartNavigation(GURL("https://example.test"));
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforeCommit, 1);
 }
 
@@ -662,7 +674,7 @@
   SimulateTimingWithoutPaint();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforePaint, 1);
 }
 
@@ -673,30 +685,30 @@
   NavigateAndCommit(GURL("https://example.test"));
   SimulateTimingWithoutPaint();
   // Now start a second navigation, but don't commit it.
-  StartNavigation(GURL("https://example.test2"));
+  tester()->StartNavigation(GURL("https://example.test2"));
   // Simulate closing the tab.
   DeleteContents();
   // The second navigation was not from GWS.
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforeCommit, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforePaint, 1);
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest,
        AbortStopBeforeCommitAndCloseBeforePaint) {
   NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
-  StartNavigation(GURL("https://example.test"));
+  tester()->StartNavigation(GURL("https://example.test"));
   // Simulate the user pressing the stop button.
   web_contents()->Stop();
   NavigateAndCommit(GURL("https://example.test2"));
   SimulateTimingWithoutPaint();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforeCommit, 1);
   // The second navigation was from GWS, as GWS was the last committed URL.
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforePaint, 1);
 }
 
@@ -707,7 +719,7 @@
   NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
   NavigateAndCommit(GURL("about:blank"));
   NavigateAndCommit(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortNewNavigationBeforePaint, 0);
 }
 
@@ -719,7 +731,7 @@
   timing.paint_timing->first_paint = base::TimeDelta::FromMicroseconds(1);
   PopulateRequiredTimingFields(&timing);
   NavigateAndCommit(GURL("https://example.test"));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // The test cannot assume that abort time will be > first_paint
   // (1 micro-sec). If the system clock is low resolution, PageLoadTracker's
@@ -729,7 +741,7 @@
   NavigateAndCommit(GURL("https://example.test2"));
 
   base::HistogramTester::CountsMap counts_map =
-      histogram_tester().GetTotalCountsForPrefix(
+      tester()->histogram_tester().GetTotalCountsForPrefix(
           internal::kHistogramFromGWSAbortNewNavigationBeforePaint);
 
   EXPECT_TRUE(counts_map.empty() ||
@@ -742,7 +754,7 @@
   SimulateTimingWithFirstPaint();
   // Simulate the user performing another navigation before paint.
   NavigateAndCommit(GURL("https://www.example.com"));
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortNewNavigationBeforeInteraction, 1);
 }
 
@@ -755,7 +767,7 @@
   // Now close the tab. This will trigger logging for the prior navigation which
   // was stopped above.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortStopBeforeInteraction, 1);
 }
 
@@ -765,7 +777,7 @@
   SimulateTimingWithFirstPaint();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforeInteraction, 1);
 }
 
@@ -775,7 +787,7 @@
   SimulateTimingWithoutPaint();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforeInteraction, 0);
 }
 
@@ -787,7 +799,7 @@
   SimulateMouseEvent();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforeInteraction, 0);
 }
 
@@ -797,16 +809,16 @@
   SimulateTimingWithFirstPaint();
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforeInteraction, 0);
 }
 
 TEST_F(FromGWSPageLoadMetricsObserverTest, ProvisionalIntent) {
   NavigateAndCommit(GURL(kGoogleSearchResultsUrl));
-  StartNavigation(GURL("intent://en.m.wikipedia.org/wiki/Test"));
+  tester()->StartNavigation(GURL("intent://en.m.wikipedia.org/wiki/Test"));
   // Simulate closing the tab.
   DeleteContents();
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramFromGWSAbortCloseBeforeCommit, 0);
 }
 
diff --git a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc
index 3732b81..c9c1003 100644
--- a/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/live_tab_count_page_load_metrics_observer_unittest.cc
@@ -70,7 +70,7 @@
       web_contents()->WasShown();
     }
 
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
   }
 
  protected:
@@ -86,7 +86,7 @@
         std::string(internal::kHistogramPrefixLiveTabCount) +
         std::string(page_load_histogram_suffix);
     for (size_t bucket = 0; bucket < expected_counts.size(); bucket++) {
-      histogram_tester().ExpectTotalCount(
+      tester()->histogram_tester().ExpectTotalCount(
           tab_count_metrics::HistogramName(histogram_prefix,
                                            /* live_tabs_only = */ true, bucket),
           expected_counts[bucket]);
diff --git a/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer_unittest.cc
index 8a1bddc..1fb6bb3 100644
--- a/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/loading_predictor_page_load_metrics_observer_unittest.cc
@@ -69,12 +69,12 @@
         .WillOnce(testing::Return(is_preconnectable));
 
     NavigateAndCommit(main_frame_url);
-    SimulateTimingUpdate(timing_);
+    tester()->SimulateTimingUpdate(timing_);
 
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramLoadingPredictorFirstContentfulPaintPreconnectable,
         is_preconnectable ? 1 : 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramLoadingPredictorFirstMeaningfulPaintPreconnectable,
         is_preconnectable ? 1 : 0);
   }
diff --git a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
index 7bcee0c..e9d5478 100644
--- a/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/local_network_requests_page_load_metrics_observer_unittest.cc
@@ -101,15 +101,16 @@
 
   void SimulateLoadedSuccessfulResource(
       const internal::PageAddressInfo& resource) {
-    SimulateLoadedResource(resource, 0);
+    SimulateLoadedResourceWithNetError(resource, 0);
   }
 
   void SimulateLoadedFailedResource(const internal::PageAddressInfo& resource) {
-    SimulateLoadedResource(resource, net::ERR_CONNECTION_REFUSED);
+    SimulateLoadedResourceWithNetError(resource, net::ERR_CONNECTION_REFUSED);
   }
 
-  void SimulateLoadedResource(const internal::PageAddressInfo& resource,
-                              const int net_error) {
+  void SimulateLoadedResourceWithNetError(
+      const internal::PageAddressInfo& resource,
+      const int net_error) {
     net::IPAddress address;
     ASSERT_TRUE(address.AssignFromIPLiteral(resource.host_ip));
     page_load_metrics::ExtraRequestCompleteInfo request_info(
@@ -121,7 +122,7 @@
         content::ResourceType::kMainFrame, net_error,
         {} /* load_timing_info */);
 
-    PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
+    tester()->SimulateLoadedResource(
         request_info, navigation_simulator_->GetGlobalRequestID());
   }
 
@@ -151,13 +152,15 @@
     for (const auto& port :
          internal::GetLocalhostHistogramNames().at(domain_type)) {
       for (const auto& histogramName : port.second) {
-        histogram_tester().ExpectUniqueSample(histogramName.second, 0, 1);
+        tester()->histogram_tester().ExpectUniqueSample(histogramName.second, 0,
+                                                        1);
       }
     }
     for (const auto& resource :
          internal::GetNonlocalhostHistogramNames().at(domain_type)) {
       for (const auto& histogramName : resource.second) {
-        histogram_tester().ExpectUniqueSample(histogramName.second, 0, 1);
+        tester()->histogram_tester().ExpectUniqueSample(histogramName.second, 0,
+                                                        1);
       }
     }
   }
@@ -166,14 +169,14 @@
     for (const auto& domain : internal::GetLocalhostHistogramNames()) {
       for (const auto& port : domain.second) {
         for (const auto& status : port.second) {
-          histogram_tester().ExpectTotalCount(status.second, 0);
+          tester()->histogram_tester().ExpectTotalCount(status.second, 0);
         }
       }
     }
     for (const auto& domain : internal::GetNonlocalhostHistogramNames()) {
       for (const auto& resource : domain.second) {
         for (const auto& status : resource.second) {
-          histogram_tester().ExpectTotalCount(status.second, 0);
+          tester()->histogram_tester().ExpectTotalCount(status.second, 0);
         }
       }
     }
@@ -181,12 +184,13 @@
 
   void ExpectUkmPageDomainMetric(const internal::PageAddressInfo& page,
                                  const internal::DomainType domain_type) {
-    auto entries = test_ukm_recorder().GetEntriesByName(
+    auto entries = tester()->test_ukm_recorder().GetEntriesByName(
         ukm::builders::PageDomainInfo::kEntryName);
     EXPECT_EQ(1u, entries.size());
     for (const auto* const entry : entries) {
-      test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(page.url));
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry,
+                                                            GURL(page.url));
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, ukm::builders::PageDomainInfo::kDomainTypeName,
           static_cast<int>(domain_type));
     }
@@ -197,23 +201,24 @@
       const std::vector<internal::UkmMetricInfo>& expected_metrics,
       const std::map<std::string, int>& expected_histograms) {
     using LocalNetworkRequests = ukm::builders::LocalNetworkRequests;
-    auto entries =
-        test_ukm_recorder().GetEntriesByName(LocalNetworkRequests::kEntryName);
+    auto entries = tester()->test_ukm_recorder().GetEntriesByName(
+        LocalNetworkRequests::kEntryName);
     ASSERT_EQ(entries.size(), expected_metrics.size());
     for (size_t i = 0; i < entries.size() && i < expected_metrics.size(); i++) {
-      test_ukm_recorder().ExpectEntrySourceHasUrl(entries[i], GURL(page.url));
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entries[i],
+                                                            GURL(page.url));
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entries[i], LocalNetworkRequests::kResourceTypeName,
           expected_metrics[i].resource_type);
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entries[i], LocalNetworkRequests::kCount_SuccessfulName,
           expected_metrics[i].success_count);
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entries[i], LocalNetworkRequests::kCount_FailedName,
           expected_metrics[i].failed_count);
       if (expected_metrics[i].resource_type ==
           internal::RESOURCE_TYPE_LOCALHOST) {
-        test_ukm_recorder().ExpectEntryMetric(
+        tester()->test_ukm_recorder().ExpectEntryMetric(
             entries[i], LocalNetworkRequests::kPortTypeName,
             static_cast<int>(expected_metrics[i].port_type));
       }
@@ -221,7 +226,8 @@
 
     // Should have generated UMA histograms for all requests made.
     for (auto hist : expected_histograms) {
-      histogram_tester().ExpectUniqueSample(hist.first, hist.second, 1);
+      tester()->histogram_tester().ExpectUniqueSample(hist.first, hist.second,
+                                                      1);
     }
   }
 
@@ -230,8 +236,8 @@
 };
 
 TEST_F(LocalNetworkRequestsPageLoadMetricsObserverTest, NoMetrics) {
-  EXPECT_EQ(0ul, test_ukm_recorder().sources_count());
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().sources_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 
   // Sanity check
   ExpectNoHistograms();
@@ -717,7 +723,8 @@
   };
   for (auto request : requests) {
     for (int i = 0; i < 100; ++i) {
-      SimulateLoadedResource(request.first, (request.second ? 0 : -1));
+      SimulateLoadedResourceWithNetError(request.first,
+                                         (request.second ? 0 : -1));
     }
   }
   for (int i = 0; i < 1000; ++i) {
@@ -727,7 +734,7 @@
   // At this point, we should still only see the domain type UKM entry.
   // Also history manipulation intervention will log a UKM for navigating away
   // from a page without user interaction.
-  EXPECT_EQ(2ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(2ul, tester()->test_ukm_recorder().entries_count());
 
   // Close the page.
   DeleteContents();
@@ -777,7 +784,7 @@
 
   // Load a resource that has the IP address in the URL but returned an empty
   // socket address for some reason.
-  PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
+  PageLoadMetricsObserverTestHarness::tester()->SimulateLoadedResource(
       {GURL(internal::kDiffSubnetRequest2.url), net::IPEndPoint(),
        -1 /* frame_tree_node_id */, true /* was_cached */,
        1024 * 20 /* raw_body_bytes */, 0 /* original_network_content_length */,
@@ -804,7 +811,7 @@
 
   // Load a resource that doesn't have the IP address in the URL and returned an
   // empty socket address (e.g., failed DNS resolution).
-  PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
+  PageLoadMetricsObserverTestHarness::tester()->SimulateLoadedResource(
       {GURL(internal::kPrivatePage.url), net::IPEndPoint(),
        -1 /* frame_tree_node_id */, false /* was_cached */,
        0 /* raw_body_bytes */, 0 /* original_network_content_length */,
@@ -896,6 +903,6 @@
   // Nothing should have been generated.
   // Note that the expected count is 1 because history manipulation intervention
   // will log a UKM for navigating away from a page without user interaction.
-  EXPECT_EQ(1ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(1ul, tester()->test_ukm_recorder().entries_count());
   ExpectNoHistograms();
 }
diff --git a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
index 25e3bfe7..a16a96c39 100644
--- a/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/media_page_load_metrics_observer_unittest.cc
@@ -51,13 +51,13 @@
     NavigateAndCommit(GURL(kDefaultTestUrl));
 
     if (simulate_play_media)
-      SimulateMediaPlayed();
+      tester()->SimulateMediaPlayed();
 
-    SimulateTimingUpdate(timing_);
+    tester()->SimulateTimingUpdate(timing_);
 
     auto resources =
         GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
-    SimulateResourceDataUseUpdate(resources);
+    tester()->SimulateResourceDataUseUpdate(resources);
     for (const auto& resource : resources) {
       if (resource->is_complete) {
         if (resource->cache_type ==
@@ -70,9 +70,9 @@
 
     if (simulate_app_background) {
       // The histograms should be logged when the app is backgrounded.
-      SimulateAppEnterBackground();
+      tester()->SimulateAppEnterBackground();
     } else {
-      NavigateToUntrackedUrl();
+      tester()->NavigateToUntrackedUrl();
     }
   }
 
@@ -96,13 +96,13 @@
   SimulatePageLoad(true /* simulate_play_media */,
                    false /* simulate_app_background */);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
       static_cast<int>(network_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache",
       static_cast<int>(cache_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total",
       static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
 }
@@ -112,13 +112,13 @@
   SimulatePageLoad(true /* simulate_play_media */,
                    true /* simulate_app_background */);
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network",
       static_cast<int>(network_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache",
       static_cast<int>(cache_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total",
       static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
 }
@@ -128,10 +128,10 @@
   SimulatePageLoad(false /* simulate_play_media */,
                    false /* simulate_app_background */);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Network", 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Cache", 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.MediaPageLoad.Experimental.Bytes.Total", 0);
 }
diff --git a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_unittest.cc
index 4457d6e0..3dfd970 100644
--- a/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/multi_tab_loading_page_load_metrics_observer_unittest.cc
@@ -70,7 +70,7 @@
       web_contents()->WasShown();
     }
 
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
   }
 
  protected:
@@ -84,14 +84,14 @@
                           base::HistogramBase::Count expected_base,
                           base::HistogramBase::Count expected_2_or_more,
                           base::HistogramBase::Count expected_5_or_more) {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         std::string(internal::kHistogramPrefixMultiTabLoading).append(suffix),
         expected_base);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         std::string(internal::kHistogramPrefixMultiTabLoading2OrMore)
             .append(suffix),
         expected_2_or_more);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         std::string(internal::kHistogramPrefixMultiTabLoading5OrMore)
             .append(suffix),
         expected_5_or_more);
diff --git a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc
index bc0035f9..79edb75 100644
--- a/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc
@@ -62,7 +62,7 @@
   void RunTest(bool is_offline_preview) {
     is_offline_preview_ = is_offline_preview;
     NavigateAndCommit(GURL(kDefaultTestUrl1));
-    SimulateTimingUpdate(timing_);
+    tester()->SimulateTimingUpdate(timing_);
 
     // Navigate again to force OnComplete, which happens when a new navigation
     // occurs.
@@ -96,10 +96,11 @@
 
   void ValidateHistogramsFor(const std::string& histogram,
                              const base::HistogramBase::Sample sample) {
-    histogram_tester().ExpectTotalCount(histogram, is_offline_preview_ ? 1 : 0);
+    tester()->histogram_tester().ExpectTotalCount(histogram,
+                                                  is_offline_preview_ ? 1 : 0);
     if (!is_offline_preview_)
       return;
-    histogram_tester().ExpectUniqueSample(histogram, sample, 1);
+    tester()->histogram_tester().ExpectUniqueSample(histogram, sample, 1);
   }
 
  protected:
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
index cdc01ced..c648438b 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc
@@ -8,15 +8,9 @@
 
 #include "base/bind.h"
 #include "base/bind_helpers.h"
-#include "chrome/test/base/testing_browser_process.h"
 #include "components/ukm/content/source_url_recorder.h"
-#include "content/public/browser/global_request_id.h"
 #include "content/public/browser/web_contents.h"
-#include "content/public/common/referrer.h"
-#include "content/public/test/web_contents_tester.h"
-#include "third_party/blink/public/platform/web_input_event.h"
 #include "url/gurl.h"
-#include "url/url_constants.h"
 
 namespace page_load_metrics {
 
@@ -40,146 +34,6 @@
   web_contents()->WasShown();
 }
 
-void PageLoadMetricsObserverTestHarness::StartNavigation(const GURL& gurl) {
-  tester_->StartNavigation(gurl);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateTimingUpdate(
-    const mojom::PageLoadTiming& timing) {
-  tester_->SimulateTimingUpdate(timing);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateTimingUpdate(
-    const mojom::PageLoadTiming& timing,
-    content::RenderFrameHost* rfh) {
-  tester_->SimulateTimingUpdate(timing, rfh);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateTimingAndMetadataUpdate(
-    const mojom::PageLoadTiming& timing,
-    const mojom::PageLoadMetadata& metadata) {
-  tester_->SimulateTimingAndMetadataUpdate(timing, metadata);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateCpuTimingUpdate(
-    const mojom::CpuTiming& cpu_timing) {
-  tester_->SimulateCpuTimingUpdate(cpu_timing);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateMetadataUpdate(
-    const mojom::PageLoadMetadata& metadata,
-    content::RenderFrameHost* rfh) {
-  tester_->SimulateMetadataUpdate(metadata, rfh);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateResourceDataUseUpdate(
-    const std::vector<mojom::ResourceDataUpdatePtr>& resources) {
-  tester_->SimulateResourceDataUseUpdate(resources);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateResourceDataUseUpdate(
-    const std::vector<mojom::ResourceDataUpdatePtr>& resources,
-    content::RenderFrameHost* render_frame_host) {
-  tester_->SimulateResourceDataUseUpdate(resources, render_frame_host);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateFeaturesUpdate(
-    const mojom::PageLoadFeatures& new_features) {
-  tester_->SimulateFeaturesUpdate(new_features);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateRenderDataUpdate(
-    const mojom::FrameRenderDataUpdate& render_data) {
-  tester_->SimulateRenderDataUpdate(render_data);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateRenderDataUpdate(
-    const mojom::FrameRenderDataUpdate& render_data,
-    content::RenderFrameHost* render_frame_host) {
-  tester_->SimulateRenderDataUpdate(render_data, render_frame_host);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
-    const ExtraRequestCompleteInfo& info) {
-  tester_->SimulateLoadedResource(info, content::GlobalRequestID());
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateLoadedResource(
-    const ExtraRequestCompleteInfo& info,
-    const content::GlobalRequestID& request_id) {
-  tester_->SimulateLoadedResource(info, request_id);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateInputEvent(
-    const blink::WebInputEvent& event) {
-  tester_->SimulateInputEvent(event);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateAppEnterBackground() {
-  tester_->SimulateAppEnterBackground();
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateMediaPlayed() {
-  tester_->SimulateMediaPlayed();
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateCookiesRead(
-    const GURL& url,
-    const GURL& first_party_url,
-    const net::CookieList& cookie_list,
-    bool blocked_by_policy) {
-  tester_->SimulateCookiesRead(url, first_party_url, cookie_list,
-                               blocked_by_policy);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateCookieChange(
-    const GURL& url,
-    const GURL& first_party_url,
-    const net::CanonicalCookie& cookie,
-    bool blocked_by_policy) {
-  tester_->SimulateCookieChange(url, first_party_url, cookie,
-                                blocked_by_policy);
-}
-
-void PageLoadMetricsObserverTestHarness::SimulateDomStorageAccess(
-    const GURL& url,
-    const GURL& first_party_url,
-    bool local,
-    bool blocked_by_policy) {
-  tester_->SimulateDomStorageAccess(url, first_party_url, local,
-                                    blocked_by_policy);
-}
-
-const base::HistogramTester&
-PageLoadMetricsObserverTestHarness::histogram_tester() const {
-  return tester_->histogram_tester();
-}
-
-MetricsWebContentsObserver* PageLoadMetricsObserverTestHarness::observer()
-    const {
-  return tester_->observer();
-}
-
-const ukm::TestAutoSetUkmRecorder&
-PageLoadMetricsObserverTestHarness::test_ukm_recorder() const {
-  return tester_->test_ukm_recorder();
-}
-
-const PageLoadMetricsObserverDelegate&
-PageLoadMetricsObserverTestHarness::GetDelegateForCommittedLoad() const {
-  return tester_->GetDelegateForCommittedLoad();
-}
-
-void PageLoadMetricsObserverTestHarness::NavigateWithPageTransitionAndCommit(
-    const GURL& url,
-    ui::PageTransition transition) {
-  tester_->NavigateWithPageTransitionAndCommit(url, transition);
-}
-
-void PageLoadMetricsObserverTestHarness::NavigateToUntrackedUrl() {
-  tester_->NavigateToUntrackedUrl();
-}
-
 const char PageLoadMetricsObserverTestHarness::kResourceUrl[] =
     "https://www.example.com/resource";
 
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
index 3398443..e114e174 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
+++ b/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h
@@ -8,42 +8,17 @@
 #include <memory>
 
 #include "base/macros.h"
-#include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
-#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
-#include "components/ukm/test_ukm_recorder.h"
-#include "ui/base/page_transition_types.h"
-
-namespace base {
-class GURL;
-}  // namespace base
-
-namespace blink {
-class WebInputEvent;
-}  // namespace blink
-
-namespace content {
-struct GlobalRequestID;
-}  // namespace content
-
-namespace mojom {
-class PageLoadMetadata;
-class PageLoadTiming;
-}  // namespace mojom
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
 
 namespace page_load_metrics {
 
-class MetricsWebContentsObserver;
 class PageLoadTracker;
 
-// This class can be used to drive tests of PageLoadMetricsObservers. To hook up
-// an observer, override RegisterObservers and call tracker->AddObserver. This
-// will attach the observer to all main frame navigations.
-//
-// This class is mostly just a wrapper around a PageLoadMetricsObserverTester.
-// Prefer to use the tester directly if you need to compose the testing logic
-// with another test harness.
+// This class can be used to drive tests of PageLoadMetricsObservers in Chrome.
+// To hook up an observer, override RegisterObservers and call
+// tracker->AddObserver. This will attach the observer to all main frame
+// navigations.
 class PageLoadMetricsObserverTestHarness
     : public ChromeRenderViewHostTestHarness {
  public:
@@ -57,86 +32,8 @@
 
   virtual void RegisterObservers(PageLoadTracker* tracker) {}
 
-  // Simulates starting a navigation to the given gurl, without committing the
-  // navigation.
-  // Note: The navigation is left in a pending state and cannot be successfully
-  // completed.
-  void StartNavigation(const GURL& gurl);
-
-  // Simulates committing a navigation to the given URL with the given
-  // PageTransition.
-  void NavigateWithPageTransitionAndCommit(const GURL& url,
-                                           ui::PageTransition transition);
-
-  // Navigates to a URL that is not tracked by page_load_metrics. Useful for
-  // forcing the OnComplete method of a PageLoadMetricsObserver to run.
-  void NavigateToUntrackedUrl();
-
-  // Call this to simulate sending a PageLoadTiming IPC from the render process
-  // to the browser process. These will update the timing information for the
-  // most recently committed navigation.
-  void SimulateTimingUpdate(const mojom::PageLoadTiming& timing);
-  void SimulateTimingUpdate(const mojom::PageLoadTiming& timing,
-                            content::RenderFrameHost* rfh);
-  void SimulateTimingAndMetadataUpdate(const mojom::PageLoadTiming& timing,
-                                       const mojom::PageLoadMetadata& metadata);
-  void SimulateMetadataUpdate(const mojom::PageLoadMetadata& metadata,
-                              content::RenderFrameHost* rfh);
-  void SimulateFeaturesUpdate(const mojom::PageLoadFeatures& new_features);
-  void SimulateCpuTimingUpdate(const mojom::CpuTiming& cpu_timing);
-  void SimulateResourceDataUseUpdate(
-      const std::vector<mojom::ResourceDataUpdatePtr>& resources);
-  void SimulateResourceDataUseUpdate(
-      const std::vector<mojom::ResourceDataUpdatePtr>& resources,
-      content::RenderFrameHost* render_frame_host);
-  void SimulateRenderDataUpdate(
-      const mojom::FrameRenderDataUpdate& render_data);
-  void SimulateRenderDataUpdate(const mojom::FrameRenderDataUpdate& render_data,
-                                content::RenderFrameHost* rfh);
-
-  // Simulates a loaded resource. Main frame resources must specify a
-  // GlobalRequestID, using the SimulateLoadedResource() method that takes a
-  // |request_id| parameter.
-  void SimulateLoadedResource(const ExtraRequestCompleteInfo& info);
-
-  // Simulates a loaded resource, with the given GlobalRequestID.
-  void SimulateLoadedResource(const ExtraRequestCompleteInfo& info,
-                              const content::GlobalRequestID& request_id);
-
-  // Simulates a user input.
-  void SimulateInputEvent(const blink::WebInputEvent& event);
-
-  // Simulates the app being backgrounded.
-  void SimulateAppEnterBackground();
-
-  // Simulate playing a media element.
-  void SimulateMediaPlayed();
-
-  // Simulate reading cookies.
-  void SimulateCookiesRead(const GURL& url,
-                           const GURL& first_party_url,
-                           const net::CookieList& cookie_list,
-                           bool blocked_by_policy);
-
-  // Simulate writing a cookie.
-  void SimulateCookieChange(const GURL& url,
-                            const GURL& first_party_url,
-                            const net::CanonicalCookie& cookie,
-                            bool blocked_by_policy);
-
-  // Simulate accessing the local storage or session storage.
-  void SimulateDomStorageAccess(const GURL& url,
-                                const GURL& first_party_url,
-                                bool local,
-                                bool blocked_by_policy);
-
-  const base::HistogramTester& histogram_tester() const;
-
-  MetricsWebContentsObserver* observer() const;
-
-  const PageLoadMetricsObserverDelegate& GetDelegateForCommittedLoad() const;
-
-  const ukm::TestAutoSetUkmRecorder& test_ukm_recorder() const;
+  PageLoadMetricsObserverTester* tester() { return tester_.get(); }
+  const PageLoadMetricsObserverTester* tester() const { return tester_.get(); }
 
  private:
   std::unique_ptr<PageLoadMetricsObserverTester> tester_;
diff --git a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc
index 3d54d8a9..b677dbd0 100644
--- a/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc
@@ -124,11 +124,12 @@
   void ValidateTimingHistogram(const std::string& histogram,
                                const base::Optional<base::TimeDelta>& event,
                                bool preview_was_active) {
-    histogram_tester().ExpectTotalCount(histogram, preview_was_active ? 1 : 0);
+    tester()->histogram_tester().ExpectTotalCount(histogram,
+                                                  preview_was_active ? 1 : 0);
     if (!preview_was_active) {
-      histogram_tester().ExpectTotalCount(histogram, 0);
+      tester()->histogram_tester().ExpectTotalCount(histogram, 0);
     } else {
-      histogram_tester().ExpectUniqueSample(
+      tester()->histogram_tester().ExpectUniqueSample(
           histogram,
           static_cast<base::HistogramBase::Sample>(
               event.value().InMilliseconds()),
@@ -140,12 +141,12 @@
                               int network_resources,
                               int64_t network_bytes) {
     if (network_resources > 0) {
-      histogram_tester().ExpectUniqueSample(
+      tester()->histogram_tester().ExpectUniqueSample(
           "PageLoad.Clients." + preview_type_name +
               ".Experimental.Bytes.NetworkIncludingHeaders",
           static_cast<int>(network_bytes / 1024), 1);
     } else {
-      histogram_tester().ExpectTotalCount(
+      tester()->histogram_tester().ExpectTotalCount(
           "PageLoad.Clients." + preview_type_name +
               ".Experimental.Bytes.NetworkIncludingHeaders",
           0);
@@ -188,10 +189,10 @@
 
   auto resources =
       GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
-  SimulateTimingUpdate(timing_);
-  NavigateToUntrackedUrl();
+  tester()->SimulateTimingUpdate(timing_);
+  tester()->NavigateToUntrackedUrl();
 
   ValidateTimingHistograms("NoScriptPreview", false /* preview_was_active */);
   ValidateTimingHistograms("ResourceLoadingHintsPreview",
@@ -212,10 +213,10 @@
 
   auto resources =
       GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
-  SimulateTimingUpdate(timing_);
-  NavigateToUntrackedUrl();
+  tester()->SimulateTimingUpdate(timing_);
+  tester()->NavigateToUntrackedUrl();
 
   ValidateTimingHistograms("NoScriptPreview", true /* preview_was_active */);
   ValidateDataHistograms("NoScriptPreview", 1 /* network_resources */,
@@ -232,10 +233,10 @@
 
   auto resources =
       GetSampleResourceDataUpdateForTesting(10 * 1024 /* resource_size */);
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
-  SimulateTimingUpdate(timing_);
-  NavigateToUntrackedUrl();
+  tester()->SimulateTimingUpdate(timing_);
+  tester()->NavigateToUntrackedUrl();
 
   ValidateTimingHistograms("ResourceLoadingHintsPreview",
                            true /* preview_was_active */);
@@ -263,7 +264,7 @@
   auto resource_data_update = ResourceDataUpdate::New();
   resource_data_update->delta_bytes = 5 * 1024;
   resources.push_back(std::move(resource_data_update));
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
   data_use += (5 * 1024);
 
   resources.clear();
@@ -271,10 +272,10 @@
   resource_data_update = ResourceDataUpdate::New();
   resource_data_update->delta_bytes = 20 * 1024;
   resources.push_back(std::move(resource_data_update));
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
   data_use += (20 * 1024);
 
-  SimulateTimingUpdate(timing_);
+  tester()->SimulateTimingUpdate(timing_);
 
   int64_t expected_savings = (data_use * inflation) / 100 + constant_savings;
 
@@ -302,7 +303,7 @@
   auto resource_data_update = ResourceDataUpdate::New();
   resource_data_update->delta_bytes = 5 * 1024;
   resources.push_back(std::move(resource_data_update));
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
   data_use += (5 * 1024);
 
   resources.clear();
@@ -310,10 +311,10 @@
   resource_data_update = ResourceDataUpdate::New();
   resource_data_update->delta_bytes = 20 * 1024;
   resources.push_back(std::move(resource_data_update));
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
   data_use += (20 * 1024);
 
-  SimulateTimingUpdate(timing_);
+  tester()->SimulateTimingUpdate(timing_);
 
   int64_t expected_savings = (data_use * inflation) / 100 + constant_savings;
 
diff --git a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
index c7598b2..ff2cab9b 100644
--- a/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc
@@ -201,7 +201,8 @@
           eligibility_reasons,
       base::Optional<base::TimeDelta> navigation_restart_penalty) {
     using UkmEntry = ukm::builders::Previews;
-    auto entries = test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName);
+    auto entries =
+        tester()->test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName);
     if (expected_recorded_previews == 0 && opt_out_value == 0 &&
         !origin_opt_out_expected && !save_data_enabled_expected &&
         !previews_likely_expected && !navigation_restart_penalty.has_value()) {
@@ -211,47 +212,50 @@
     EXPECT_EQ(1u, entries.size());
 
     const auto* const entry = entries.front();
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kDefaultTestUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
+        entry, GURL(kDefaultTestUrl));
 
     // Collect the set of recorded previews into a PreviewsState bitmask to
     // compare against the expected previews.
     content::PreviewsState recorded_previews = 0;
-    if (test_ukm_recorder().EntryHasMetric(entry,
-                                           UkmEntry::koffline_previewName))
+    if (tester()->test_ukm_recorder().EntryHasMetric(
+            entry, UkmEntry::koffline_previewName))
       recorded_previews |= content::OFFLINE_PAGE_ON;
-    if (test_ukm_recorder().EntryHasMetric(entry, UkmEntry::klite_pageName))
+    if (tester()->test_ukm_recorder().EntryHasMetric(entry,
+                                                     UkmEntry::klite_pageName))
       recorded_previews |= content::SERVER_LITE_PAGE_ON;
-    if (test_ukm_recorder().EntryHasMetric(entry,
-                                           UkmEntry::klite_page_redirectName)) {
+    if (tester()->test_ukm_recorder().EntryHasMetric(
+            entry, UkmEntry::klite_page_redirectName)) {
       recorded_previews |= content::LITE_PAGE_REDIRECT_ON;
     }
-    if (test_ukm_recorder().EntryHasMetric(entry, UkmEntry::knoscriptName))
+    if (tester()->test_ukm_recorder().EntryHasMetric(entry,
+                                                     UkmEntry::knoscriptName))
       recorded_previews |= content::NOSCRIPT_ON;
-    if (test_ukm_recorder().EntryHasMetric(
+    if (tester()->test_ukm_recorder().EntryHasMetric(
             entry, UkmEntry::kresource_loading_hintsName))
       recorded_previews |= content::RESOURCE_LOADING_HINTS_ON;
-    if (test_ukm_recorder().EntryHasMetric(entry,
-                                           UkmEntry::kdefer_all_scriptName))
+    if (tester()->test_ukm_recorder().EntryHasMetric(
+            entry, UkmEntry::kdefer_all_scriptName))
       recorded_previews |= content::DEFER_ALL_SCRIPT_ON;
     EXPECT_EQ(expected_recorded_previews, recorded_previews);
 
-    EXPECT_EQ(opt_out_value != 0, test_ukm_recorder().EntryHasMetric(
+    EXPECT_EQ(opt_out_value != 0, tester()->test_ukm_recorder().EntryHasMetric(
                                       entry, UkmEntry::kopt_outName));
     if (opt_out_value != 0) {
-      test_ukm_recorder().ExpectEntryMetric(entry, UkmEntry::kopt_outName,
-                                            opt_out_value);
+      tester()->test_ukm_recorder().ExpectEntryMetric(
+          entry, UkmEntry::kopt_outName, opt_out_value);
     }
     EXPECT_EQ(origin_opt_out_expected,
-              test_ukm_recorder().EntryHasMetric(
+              tester()->test_ukm_recorder().EntryHasMetric(
                   entry, UkmEntry::korigin_opt_outName));
     EXPECT_EQ(save_data_enabled_expected,
-              test_ukm_recorder().EntryHasMetric(
+              tester()->test_ukm_recorder().EntryHasMetric(
                   entry, UkmEntry::ksave_data_enabledName));
     EXPECT_EQ(previews_likely_expected,
-              test_ukm_recorder().EntryHasMetric(
+              tester()->test_ukm_recorder().EntryHasMetric(
                   entry, UkmEntry::kpreviews_likelyName));
     if (navigation_restart_penalty.has_value()) {
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::knavigation_restart_penaltyName,
           navigation_restart_penalty.value().InMilliseconds());
     }
@@ -259,55 +263,55 @@
     int want_lite_page_eligibility_reason =
         static_cast<int>(eligibility_reasons[PreviewsType::LITE_PAGE]);
     if (want_lite_page_eligibility_reason) {
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::kproxy_lite_page_eligibility_reasonName,
           want_lite_page_eligibility_reason);
     } else {
-      EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+      EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
           entry, UkmEntry::kproxy_lite_page_eligibility_reasonName));
     }
 
     int want_lite_page_redirect_eligibility_reason =
         static_cast<int>(eligibility_reasons[PreviewsType::LITE_PAGE_REDIRECT]);
     if (want_lite_page_redirect_eligibility_reason) {
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::klite_page_redirect_eligibility_reasonName,
           want_lite_page_redirect_eligibility_reason);
     } else {
-      EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+      EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
           entry, UkmEntry::klite_page_redirect_eligibility_reasonName));
     }
 
     int want_noscript_eligibility_reason =
         static_cast<int>(eligibility_reasons[PreviewsType::NOSCRIPT]);
     if (want_noscript_eligibility_reason) {
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::knoscript_eligibility_reasonName,
           want_noscript_eligibility_reason);
     } else {
-      EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+      EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
           entry, UkmEntry::knoscript_eligibility_reasonName));
     }
 
     int want_resource_loading_hints_eligibility_reason = static_cast<int>(
         eligibility_reasons[PreviewsType::RESOURCE_LOADING_HINTS]);
     if (want_resource_loading_hints_eligibility_reason) {
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::kresource_loading_hints_eligibility_reasonName,
           want_resource_loading_hints_eligibility_reason);
     } else {
-      EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+      EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
           entry, UkmEntry::kresource_loading_hints_eligibility_reasonName));
     }
 
     int want_offline_eligibility_reason =
         static_cast<int>(eligibility_reasons[PreviewsType::OFFLINE]);
     if (want_offline_eligibility_reason) {
-      test_ukm_recorder().ExpectEntryMetric(
+      tester()->test_ukm_recorder().ExpectEntryMetric(
           entry, UkmEntry::koffline_eligibility_reasonName,
           want_offline_eligibility_reason);
     } else {
-      EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+      EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
           entry, UkmEntry::koffline_eligibility_reasonName));
     }
   }
@@ -316,7 +320,8 @@
       base::Optional<int64_t> hint_generation_timestamp,
       base::Optional<int> hint_source) {
     using UkmEntry = ukm::builders::OptimizationGuide;
-    auto entries = test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName);
+    auto entries =
+        tester()->test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName);
     if (!hint_generation_timestamp.has_value() && !hint_source.has_value()) {
       EXPECT_EQ(0u, entries.size());
       return;
@@ -324,20 +329,21 @@
 
     EXPECT_EQ(1u, entries.size());
     for (const auto* const entry : entries) {
-      test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kDefaultTestUrl));
+      tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
+          entry, GURL(kDefaultTestUrl));
       if (hint_generation_timestamp.has_value()) {
-        test_ukm_recorder().ExpectEntryMetric(
+        tester()->test_ukm_recorder().ExpectEntryMetric(
             entry, UkmEntry::kHintGenerationTimestampName,
             hint_generation_timestamp.value());
       } else {
-        EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+        EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
             entry, UkmEntry::kHintGenerationTimestampName));
       }
       if (hint_source.has_value()) {
-        test_ukm_recorder().ExpectEntryMetric(entry, UkmEntry::kHintSourceName,
-                                              hint_source.value());
+        tester()->test_ukm_recorder().ExpectEntryMetric(
+            entry, UkmEntry::kHintSourceName, hint_source.value());
       } else {
-        EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+        EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
             entry, UkmEntry::kHintSourceName));
       }
     }
@@ -362,7 +368,7 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -380,8 +386,9 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
-  observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
-  NavigateToUntrackedUrl();
+  tester()->metrics_web_contents_observer()->BroadcastEventToObservers(
+      PreviewsUITabHelper::OptOutEventKey());
+  tester()->NavigateToUntrackedUrl();
 
   // Opt out should not be added since we don't track this type.
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
@@ -402,7 +409,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::SERVER_LITE_PAGE_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -421,8 +428,9 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
-  NavigateToUntrackedUrl();
+  tester()->metrics_web_contents_observer()->BroadcastEventToObservers(
+      PreviewsUITabHelper::OptOutEventKey());
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::SERVER_LITE_PAGE_ON, 2 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -442,7 +450,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::LITE_PAGE_REDIRECT_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -461,8 +469,9 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
-  NavigateToUntrackedUrl();
+  tester()->metrics_web_contents_observer()->BroadcastEventToObservers(
+      PreviewsUITabHelper::OptOutEventKey());
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::LITE_PAGE_REDIRECT_ON, 2 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -479,7 +488,7 @@
           false /* save_data_enabled */, {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, "badversion");
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::NOSCRIPT_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -498,8 +507,9 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
-  NavigateToUntrackedUrl();
+  tester()->metrics_web_contents_observer()->BroadcastEventToObservers(
+      PreviewsUITabHelper::OptOutEventKey());
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::NOSCRIPT_ON, 2 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -518,7 +528,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -537,7 +547,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::RESOURCE_LOADING_HINTS_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -556,8 +566,9 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
-  NavigateToUntrackedUrl();
+  tester()->metrics_web_contents_observer()->BroadcastEventToObservers(
+      PreviewsUITabHelper::OptOutEventKey());
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::RESOURCE_LOADING_HINTS_ON, 2 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -576,7 +587,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::DEFER_ALL_SCRIPT_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -595,8 +606,9 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  observer()->BroadcastEventToObservers(PreviewsUITabHelper::OptOutEventKey());
-  NavigateToUntrackedUrl();
+  tester()->metrics_web_contents_observer()->BroadcastEventToObservers(
+      PreviewsUITabHelper::OptOutEventKey());
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::DEFER_ALL_SCRIPT_ON, 2 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -615,7 +627,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               true /* origin_opt_out_expected */,
@@ -634,7 +646,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -656,7 +668,7 @@
       base::TimeDelta::FromMilliseconds(1337) /* navigation_restart_penalty */,
       base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(
       content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
@@ -676,7 +688,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -694,7 +706,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -712,7 +724,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -731,7 +743,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -750,7 +762,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::OFFLINE_PAGE_ON, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -778,7 +790,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -813,7 +825,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -845,7 +857,7 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, hint_version_string);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -870,7 +882,7 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, hint_version_string);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -889,7 +901,7 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */, "notahintversion");
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -927,7 +939,7 @@
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
 
-  SimulateAppEnterBackground();
+  tester()->SimulateAppEnterBackground();
 
   ValidateUKM(content::PREVIEWS_UNSPECIFIED, 0 /* opt_out_value */,
               false /* origin_opt_out_expected */,
@@ -941,7 +953,7 @@
 TEST_F(PreviewsUKMObserverTest, TestPageEndReasonUMA) {
   std::unique_ptr<base::StatisticsRecorder> recorder(
       base::StatisticsRecorder::CreateTemporaryForTesting());
-  base::HistogramTester tester;
+  base::HistogramTester histogram_tester;
 
   // No preview:
   RunTest(content::PREVIEWS_OFF /* committed_state */,
@@ -950,12 +962,12 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
-  NavigateToUntrackedUrl();
-  tester.ExpectUniqueSample(
+  tester()->NavigateToUntrackedUrl();
+  histogram_tester.ExpectUniqueSample(
       "Previews.PageEndReason.None",
       page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
   // The top level metric is not recorded on a non-preview.
-  tester.ExpectTotalCount("Previews.PageEndReason", 0);
+  histogram_tester.ExpectTotalCount("Previews.PageEndReason", 0);
 
   // Lite Page Redirect:
   RunTest(content::LITE_PAGE_REDIRECT_ON /* committed_state */,
@@ -964,13 +976,13 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
-  NavigateToUntrackedUrl();
-  tester.ExpectUniqueSample(
+  tester()->NavigateToUntrackedUrl();
+  histogram_tester.ExpectUniqueSample(
       "Previews.PageEndReason.LitePageRedirect",
       page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
-  tester.ExpectBucketCount("Previews.PageEndReason",
-                           page_load_metrics::PageEndReason::END_NEW_NAVIGATION,
-                           1);
+  histogram_tester.ExpectBucketCount(
+      "Previews.PageEndReason",
+      page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
 
   // Defer All Script:
   RunTest(content::DEFER_ALL_SCRIPT_ON /* committed_state */,
@@ -979,13 +991,13 @@
           {} /* eligibility_reasons */,
           base::nullopt /* navigation_restart_penalty */,
           base::nullopt /* hint_version_string */);
-  NavigateToUntrackedUrl();
-  tester.ExpectUniqueSample(
+  tester()->NavigateToUntrackedUrl();
+  histogram_tester.ExpectUniqueSample(
       "Previews.PageEndReason.DeferAllScript",
       page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 1);
-  tester.ExpectBucketCount("Previews.PageEndReason",
-                           page_load_metrics::PageEndReason::END_NEW_NAVIGATION,
-                           2);
+  histogram_tester.ExpectBucketCount(
+      "Previews.PageEndReason",
+      page_load_metrics::PageEndReason::END_NEW_NAVIGATION, 2);
 }
 
 }  // namespace
diff --git a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer_unittest.cc
index 378d0057..f430fc48 100644
--- a/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/protocol_page_load_metrics_observer_unittest.cc
@@ -49,7 +49,7 @@
 
     page_load_metrics::mojom::PageLoadTiming timing;
     InitializeTestPageLoadTiming(&timing);
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
 
     // Navigate again to force OnComplete, which happens when a new navigation
     // occurs.
@@ -60,7 +60,7 @@
     int count = 0;
 
     base::HistogramTester::CountsMap counts_map =
-        histogram_tester().GetTotalCountsForPrefix(
+        tester()->histogram_tester().GetTotalCountsForPrefix(
             "PageLoad.Clients.Protocol.");
     for (const auto& entry : counts_map)
       count += entry.second;
@@ -75,21 +75,21 @@
     std::string prefix = "PageLoad.Clients.Protocol.";
     prefix += protocol;
 
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".ParseTiming.NavigationToParseStart", 1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".PaintTiming.ParseStartToFirstContentfulPaint", 1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".PaintTiming.NavigationToFirstContentfulPaint", 1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".Experimental.PaintTiming.ParseStartToFirstMeaningfulPaint",
         1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".Experimental.PaintTiming.NavigationToFirstMeaningfulPaint",
         1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".DocumentTiming.NavigationToDOMContentLoadedEventFired", 1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".DocumentTiming.NavigationToLoadEventFired", 1);
   }
 
diff --git a/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer_unittest.cc
index 3a534e5ca..ae89dbd 100644
--- a/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/scheme_page_load_metrics_observer_unittest.cc
@@ -48,7 +48,7 @@
 
     page_load_metrics::mojom::PageLoadTiming timing;
     InitializeTestPageLoadTiming(&timing);
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
 
     // Navigate again to force OnComplete, which happens when a new navigation
     // occurs.
@@ -59,13 +59,14 @@
   int CountTotalProtocolMetricsRecorded(const std::string& protocol) {
     int count = 0;
     base::HistogramTester::CountsMap counts_map =
-        histogram_tester().GetTotalCountsForPrefix("PageLoad.Clients.Scheme.");
+        tester()->histogram_tester().GetTotalCountsForPrefix(
+            "PageLoad.Clients.Scheme.");
     for (const auto& entry : counts_map)
       count += entry.second;
 
     int understat_count = 0;
     base::HistogramTester::CountsMap understat_counts_map =
-        histogram_tester().GetTotalCountsForPrefix(
+        tester()->histogram_tester().GetTotalCountsForPrefix(
             "PageLoad.Clients.Scheme." + base::ToUpperASCII(protocol) +
             ".PaintTiming.UnderStat");
     for (const auto& entry : understat_counts_map)
@@ -77,10 +78,10 @@
   // Returns the value of the sample present in |histogram_name|. Should be
   // called only if |histogram_name| contains exactly 1 sample.
   int32_t GetRecordedMetricValue(const std::string& histogram_name) const {
-    histogram_tester().ExpectTotalCount(histogram_name, 1);
+    tester()->histogram_tester().ExpectTotalCount(histogram_name, 1);
 
     std::vector<base::Bucket> buckets =
-        histogram_tester().GetAllSamples(histogram_name);
+        tester()->histogram_tester().GetAllSamples(histogram_name);
     for (const auto& bucket : buckets) {
       if (bucket.count == 1) {
         return bucket.min;
@@ -106,25 +107,26 @@
     std::string fcp_understat_new_nav_histogram_name(
         fcp_understat_histogram_name + ".UserInitiated.NewNavigation");
 
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".ParseTiming.NavigationToParseStart", 1);
-    histogram_tester().ExpectTotalCount(fcp_histogram_name, 1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(fcp_histogram_name, 1);
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".PaintTiming.ParseStartToFirstContentfulPaint", 1);
-    histogram_tester().ExpectUniqueSample(
+    tester()->histogram_tester().ExpectUniqueSample(
         prefix + ".PaintTiming.ParseStartToFirstContentfulPaint",
         static_cast<base::HistogramBase::Sample>(200), 1);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         prefix + ".Experimental.PaintTiming.NavigationToFirstMeaningfulPaint",
         1);
 
-    histogram_tester().ExpectBucketCount(fcp_understat_histogram_name, 0, 1);
+    tester()->histogram_tester().ExpectBucketCount(fcp_understat_histogram_name,
+                                                   0, 1);
     if (new_navigation) {
-      histogram_tester().ExpectBucketCount(fcp_understat_new_nav_histogram_name,
-                                           0, 1);
+      tester()->histogram_tester().ExpectBucketCount(
+          fcp_understat_new_nav_histogram_name, 0, 1);
     } else {
-      histogram_tester().ExpectTotalCount(fcp_understat_new_nav_histogram_name,
-                                          0);
+      tester()->histogram_tester().ExpectTotalCount(
+          fcp_understat_new_nav_histogram_name, 0);
     }
 
     // Must remain synchronized with the array of the same name in
@@ -140,10 +142,10 @@
       base::TimeDelta threshold(base::TimeDelta::FromSeconds(
           kUnderStatRecordingIntervalsSeconds[index]));
       if (recorded_fcp_value <= threshold) {
-        histogram_tester().ExpectBucketCount(fcp_understat_histogram_name,
-                                             index + 1, 1);
+        tester()->histogram_tester().ExpectBucketCount(
+            fcp_understat_histogram_name, index + 1, 1);
         if (new_navigation) {
-          histogram_tester().ExpectBucketCount(
+          tester()->histogram_tester().ExpectBucketCount(
               fcp_understat_new_nav_histogram_name, index + 1, 1);
         }
       }
@@ -152,7 +154,7 @@
     // Overflow bucket should be empty. This also ensures that
     // kUnderStatRecordingIntervalsSeconds above is synchronized with the array
     // of the same name in scheme_page_load_metrics_observer.cc.
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         fcp_understat_histogram_name,
         base::size(kUnderStatRecordingIntervalsSeconds) + 1, 0);
   }
diff --git a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
index bbacaa6..a4f4f53 100644
--- a/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/service_worker_page_load_metrics_observer_unittest.cc
@@ -32,93 +32,93 @@
     page_load_metrics::mojom::PageLoadTiming timing;
     page_load_metrics::InitPageLoadTimingForTest(&timing);
     timing.navigation_start = base::Time::FromDoubleT(1);
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
   }
 
   void AssertNoServiceWorkerHistogramsLogged() {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstInputDelay, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstPaint, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstContentfulPaint, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kBackgroundHistogramServiceWorkerFirstContentfulPaint, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstContentfulPaintForwardBack, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstContentfulPaintForwardBackNoStore,
         0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstMeaningfulPaint, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaint, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerDomContentLoaded, 0);
-    histogram_tester().ExpectTotalCount(internal::kHistogramServiceWorkerLoad,
-                                        0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
+        internal::kHistogramServiceWorkerLoad, 0);
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStart, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kBackgroundHistogramServiceWorkerParseStart, 0);
   }
 
   void AssertNoInboxHistogramsLogged() {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartInbox, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstContentfulPaintInbox, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintInbox,
         0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstMeaningfulPaintInbox, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaintInbox,
         0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerDomContentLoadedInbox, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerLoadInbox, 0);
   }
 
   void AssertNoSearchHistogramsLogged() {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartSearch, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstContentfulPaintSearch, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintSearch,
         0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerFirstMeaningfulPaintSearch, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaintSearch,
         0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerDomContentLoadedSearch, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramServiceWorkerLoadSearch, 0);
   }
 
   void AssertNoSearchNoSWHistogramsLogged() {
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramNoServiceWorkerFirstContentfulPaintSearch, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::
             kHistogramNoServiceWorkerParseStartToFirstContentfulPaintSearch,
         0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramNoServiceWorkerFirstMeaningfulPaintSearch, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::
             kHistogramNoServiceWorkerParseStartToFirstMeaningfulPaintSearch,
         0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramNoServiceWorkerDomContentLoadedSearch, 0);
-    histogram_tester().ExpectTotalCount(
+    tester()->histogram_tester().ExpectTotalCount(
         internal::kHistogramNoServiceWorkerLoadSearch, 0);
   }
 
@@ -149,7 +149,7 @@
   AssertNoInboxHistogramsLogged();
   AssertNoSearchHistogramsLogged();
   AssertNoSearchNoSWHistogramsLogged();
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(ServiceWorkerPageLoadMetricsObserverTest, NoServiceWorker) {
@@ -157,13 +157,13 @@
   InitializeTestPageLoadTiming(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   AssertNoServiceWorkerHistogramsLogged();
   AssertNoInboxHistogramsLogged();
   AssertNoSearchHistogramsLogged();
   AssertNoSearchNoSWHistogramsLogged();
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(ServiceWorkerPageLoadMetricsObserverTest, WithServiceWorker) {
@@ -174,66 +174,68 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags |=
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorServiceWorkerControlled;
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstInputDelay, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstInputDelay,
       timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kBackgroundHistogramServiceWorkerFirstContentfulPaint, 0);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramServiceWorkerLoad, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerLoad, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartForwardBack, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartForwardBackNoStore, 0);
 
-  const auto& entries = test_ukm_recorder().GetEntriesByName(
+  const auto& entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_ServiceWorkerControlled::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kDefaultTestUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
+        entry, GURL(kDefaultTestUrl));
   }
 
   AssertNoInboxHistogramsLogged();
@@ -251,43 +253,45 @@
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorServiceWorkerControlled;
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
   // Background the tab, then forground it.
   web_contents()->WasHidden();
   web_contents()->WasShown();
 
   InitializeTestPageLoadTiming(&timing);
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstPaint, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kBackgroundHistogramServiceWorkerFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kBackgroundHistogramServiceWorkerFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaint, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaint, 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerDomContentLoaded, 0);
-  histogram_tester().ExpectTotalCount(internal::kHistogramServiceWorkerLoad, 0);
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerLoad, 0);
   // TODO(crbug.com/686590): The following expectation fails on Win7 Tests
   // (dbg)(1) builder, so is disabled for the time being.
-  // histogram_tester().ExpectTotalCount(
+  // tester()->histogram_tester().ExpectTotalCount(
   //     internal::kBackgroundHistogramServiceWorkerParseStart, 1);
 
-  const auto& entries = test_ukm_recorder().GetEntriesByName(
+  const auto& entries = tester()->test_ukm_recorder().GetEntriesByName(
       ukm::builders::PageLoad_ServiceWorkerControlled::kEntryName);
   EXPECT_EQ(1u, entries.size());
   for (const auto* entry : entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(entry, GURL(kDefaultTestUrl));
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(
+        entry, GURL(kDefaultTestUrl));
   }
 
   AssertNoInboxHistogramsLogged();
@@ -303,113 +307,114 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags |=
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorServiceWorkerControlled;
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartInbox, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartInbox,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaintInbox, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaintInbox,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kBackgroundHistogramServiceWorkerFirstContentfulPaint, 0);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintInbox,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintInbox,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaint,
       timing.paint_timing->first_meaningful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaintInbox, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaintInbox,
       timing.paint_timing->first_meaningful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaint,
       (timing.paint_timing->first_meaningful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaintInbox,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaintInbox,
       (timing.paint_timing->first_meaningful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerDomContentLoadedInbox, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerDomContentLoadedInbox,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramServiceWorkerLoad, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerLoad, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerLoadInbox, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerLoadInbox,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
@@ -425,113 +430,114 @@
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags |=
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorServiceWorkerControlled;
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartSearch,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaintSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaintSearch,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kBackgroundHistogramServiceWorkerFirstContentfulPaint, 0);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintSearch,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstContentfulPaintSearch,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaint,
       timing.paint_timing->first_meaningful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaintSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstMeaningfulPaintSearch,
       timing.paint_timing->first_meaningful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaint,
       (timing.paint_timing->first_meaningful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaintSearch,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartToFirstMeaningfulPaintSearch,
       (timing.paint_timing->first_meaningful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerDomContentLoadedSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerDomContentLoadedSearch,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramServiceWorkerLoad, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramServiceWorkerLoad, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerLoadSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerLoadSearch,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
@@ -545,51 +551,51 @@
 
   NavigateAndCommit(GURL(kSearchTestUrl));
   page_load_metrics::mojom::PageLoadMetadata metadata;
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNoServiceWorkerFirstContentfulPaintSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNoServiceWorkerFirstContentfulPaintSearch,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNoServiceWorkerParseStartToFirstContentfulPaintSearch,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNoServiceWorkerParseStartToFirstContentfulPaintSearch,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNoServiceWorkerFirstMeaningfulPaintSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNoServiceWorkerFirstMeaningfulPaintSearch,
       timing.paint_timing->first_meaningful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNoServiceWorkerParseStartToFirstMeaningfulPaintSearch,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNoServiceWorkerParseStartToFirstMeaningfulPaintSearch,
       (timing.paint_timing->first_meaningful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNoServiceWorkerDomContentLoadedSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNoServiceWorkerDomContentLoadedSearch,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNoServiceWorkerLoadSearch, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNoServiceWorkerLoadSearch,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
 
@@ -607,40 +613,40 @@
   // of PAGE_TRANSITION_RELOAD with a PAGE_TRANSITION_FORWARD_BACK
   // modifier. This test verifies that when we encounter such a page, we log it
   // as a forward/back navigation.
-  NavigateWithPageTransitionAndCommit(
+  tester()->NavigateWithPageTransitionAndCommit(
       GURL(kDefaultTestUrl),
       ui::PageTransitionFromInt(ui::PAGE_TRANSITION_RELOAD |
                                 ui::PAGE_TRANSITION_FORWARD_BACK));
   page_load_metrics::mojom::PageLoadMetadata metadata;
   metadata.behavior_flags |=
       blink::WebLoadingBehaviorFlag::kWebLoadingBehaviorServiceWorkerControlled;
-  SimulateTimingAndMetadataUpdate(timing, metadata);
+  tester()->SimulateTimingAndMetadataUpdate(timing, metadata);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerFirstContentfulPaintForwardBack, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerFirstContentfulPaintForwardBack,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramServiceWorkerParseStartForwardBack, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramServiceWorkerParseStartForwardBack,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 }
diff --git a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc
index c557849..6c40c47 100644
--- a/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/session_restore_page_load_metrics_observer_unittest.cc
@@ -19,11 +19,11 @@
 #include "base/time/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h"
 #include "chrome/browser/resource_coordinator/tab_manager.h"
 #include "chrome/browser/sessions/session_restore.h"
 #include "chrome/test/base/chrome_render_view_host_test_harness.h"
 #include "chrome/test/base/testing_profile.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/page_load_metrics/common/test/page_load_metrics_test_util.h"
 #include "content/public/browser/browser_url_handler.h"
@@ -160,7 +160,7 @@
 
 TEST_F(SessionRestorePageLoadMetricsObserverTest, NoMetrics) {
   ExpectFirstPaintMetricsTotalCount(0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(SessionRestorePageLoadMetricsObserverTest,
@@ -169,7 +169,7 @@
       GetTestURL(), web_contents()->GetMainFrame());
   ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
   ExpectFirstPaintMetricsTotalCount(0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(SessionRestorePageLoadMetricsObserverTest, RestoreSingleForegroundTab) {
@@ -177,9 +177,9 @@
   ASSERT_NO_FATAL_FAILURE(RestoreTab(web_contents()));
   ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
   ExpectFirstPaintMetricsTotalCount(1);
-  EXPECT_EQ(1ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(1ul, tester()->test_ukm_recorder().entries_count());
   ukm::TestUkmRecorder::ExpectEntryMetric(
-      test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName)[0],
+      tester()->test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName)[0],
       UkmEntry::kSessionRestoreTabCountName, 1);
 }
 
@@ -193,9 +193,9 @@
     ASSERT_NO_FATAL_FAILURE(RestoreTab(contents));
     ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(contents));
     ExpectFirstPaintMetricsTotalCount(i + 1);
-    EXPECT_EQ(i + 1, test_ukm_recorder().entries_count());
+    EXPECT_EQ(i + 1, tester()->test_ukm_recorder().entries_count());
     ukm::TestUkmRecorder::ExpectEntryMetric(
-        test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName)[i],
+        tester()->test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName)[i],
         UkmEntry::kSessionRestoreTabCountName, i + 1);
   }
 }
@@ -210,7 +210,7 @@
 
   // No paint timings recorded for tabs restored in background.
   ExpectFirstPaintMetricsTotalCount(0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(SessionRestorePageLoadMetricsObserverTest, HideTabBeforeFirstPaints) {
@@ -243,7 +243,7 @@
   // No paint timings recorded because the initial foreground tab was hidden.
   ASSERT_NO_FATAL_FAILURE(SimulateTimingUpdateForTab(web_contents()));
   ExpectFirstPaintMetricsTotalCount(0);
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(SessionRestorePageLoadMetricsObserverTest, MultipleSessionRestores) {
@@ -256,9 +256,10 @@
 
     // Number of paint timings should match the number of session restores.
     ExpectFirstPaintMetricsTotalCount(i);
-    EXPECT_EQ(i, test_ukm_recorder().entries_count());
+    EXPECT_EQ(i, tester()->test_ukm_recorder().entries_count());
     ukm::TestUkmRecorder::ExpectEntryMetric(
-        test_ukm_recorder().GetEntriesByName(UkmEntry::kEntryName)[i - 1],
+        tester()->test_ukm_recorder().GetEntriesByName(
+            UkmEntry::kEntryName)[i - 1],
         UkmEntry::kSessionRestoreTabCountName, i);
   }
 }
diff --git a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
index 651b22b..6727b99 100644
--- a/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/signed_exchange_page_load_metrics_observer_unittest.cc
@@ -39,7 +39,7 @@
 
   void AssertNoSignedExchangeHistogramsLoggedFor(const std::string& prefix) {
     base::HistogramTester::CountsMap empty_counts_map =
-        histogram_tester().GetTotalCountsForPrefix(prefix);
+        tester()->histogram_tester().GetTotalCountsForPrefix(prefix);
     for (const auto& it : empty_counts_map) {
       base::HistogramBase::Count count = it.second;
       EXPECT_EQ(0, count) << "Histogram \"" << it.first
@@ -87,7 +87,7 @@
   InitializeTestPageLoadTiming(&timing);
 
   NavigateAndCommit(GURL(kDefaultTestUrl));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   AssertNoSignedExchangeHistogramsLogged();
   AssertNoSignedExchangeHistogramsLoggedFor(
@@ -101,84 +101,84 @@
   InitializeTestPageLoadTiming(&timing);
 
   NavigateAndCommitSignedExchange(GURL(kDefaultTestUrl), false);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeFirstInputDelay, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeFirstInputDelay,
       timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramSignedExchangeLoad,
-                                      1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeLoad, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNotCachedSignedExchangeParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNotCachedSignedExchangeParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNotCachedSignedExchangeFirstInputDelay, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNotCachedSignedExchangeFirstInputDelay,
       timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNotCachedSignedExchangeFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNotCachedSignedExchangeFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNotCachedSignedExchangeFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNotCachedSignedExchangeFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::
           kHistogramNotCachedSignedExchangeParseStartToFirstContentfulPaint,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::
           kHistogramNotCachedSignedExchangeParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
@@ -186,23 +186,23 @@
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNotCachedSignedExchangeDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNotCachedSignedExchangeDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNotCachedSignedExchangeLoad, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNotCachedSignedExchangeLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramNotCachedSignedExchangeParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramNotCachedSignedExchangeParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
@@ -215,100 +215,100 @@
   InitializeTestPageLoadTiming(&timing);
 
   NavigateAndCommitSignedExchange(GURL(kDefaultTestUrl), true);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeFirstInputDelay, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeFirstInputDelay,
       timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeParseStartToFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramSignedExchangeDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(internal::kHistogramSignedExchangeLoad,
-                                      1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectTotalCount(
+      internal::kHistogramSignedExchangeLoad, 1);
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramSignedExchangeLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeFirstInputDelay, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeFirstInputDelay,
       timing.interactive_timing->first_input_delay.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeFirstPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeFirstPaint,
       timing.paint_timing->first_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeFirstContentfulPaint, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeFirstContentfulPaint,
       timing.paint_timing->first_contentful_paint.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeParseStartToFirstContentfulPaint,
       1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeParseStartToFirstContentfulPaint,
       (timing.paint_timing->first_contentful_paint.value() -
        timing.parse_timing->parse_start.value())
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeDomContentLoaded, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeDomContentLoaded,
       timing.document_timing->dom_content_loaded_event_start.value()
           .InMilliseconds(),
       1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeLoad, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeLoad,
       timing.document_timing->load_event_start.value().InMilliseconds(), 1);
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       internal::kHistogramCachedSignedExchangeParseStart, 1);
-  histogram_tester().ExpectBucketCount(
+  tester()->histogram_tester().ExpectBucketCount(
       internal::kHistogramCachedSignedExchangeParseStart,
       timing.parse_timing->parse_start.value().InMilliseconds(), 1);
 
@@ -323,14 +323,14 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommitSignedExchange(GURL(kDefaultTestUrl), true);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Background the tab, then foreground it.
   web_contents()->WasHidden();
   web_contents()->WasShown();
 
   InitializeTestPageLoadTiming(&timing);
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   AssertNoSignedExchangeHistogramsLogged();
   AssertNoSignedExchangeHistogramsLoggedFor(
diff --git a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc
index 09e43b0d..ac3f1d89 100644
--- a/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/subresource_loading_page_load_metrics_observer_unittest.cc
@@ -44,7 +44,7 @@
     }
 
     NavigateAndCommit(navigation_url_);
-    SimulateTimingUpdate(timing_);
+    tester()->SimulateTimingUpdate(timing_);
   }
 
   page_load_metrics::mojom::ResourceDataUpdatePtr CreateCSSResource(
@@ -131,13 +131,13 @@
                                         true /* is_complete */,
                                         true /* completed_before_fcp */));
 
-  SimulateResourceDataUseUpdate(resources);
-  NavigateToUntrackedUrl();
+  tester()->SimulateResourceDataUseUpdate(resources);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 2,
       1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 3, 1);
 }
 
@@ -162,13 +162,13 @@
                                        true /* is_complete */,
                                        true /* completed_before_fcp */));
 
-  SimulateResourceDataUseUpdate(resources);
-  NavigateToUntrackedUrl();
+  tester()->SimulateResourceDataUseUpdate(resources);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 2,
       1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 3, 1);
 }
 
@@ -193,13 +193,13 @@
                                           true /* is_complete */,
                                           true /* completed_before_fcp */));
 
-  SimulateResourceDataUseUpdate(resources);
-  NavigateToUntrackedUrl();
+  tester()->SimulateResourceDataUseUpdate(resources);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 0,
       1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 0, 1);
 }
 
@@ -224,13 +224,13 @@
                                         false /* is_complete */,
                                         false /* completed_before_fcp */));
 
-  SimulateResourceDataUseUpdate(resources);
-  NavigateToUntrackedUrl();
+  tester()->SimulateResourceDataUseUpdate(resources);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 0,
       1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 0, 1);
 }
 
@@ -256,13 +256,13 @@
                                         true /* is_complete */,
                                         true /* completed_before_fcp */));
 
-  SimulateResourceDataUseUpdate(resources);
-  NavigateToUntrackedUrl();
+  tester()->SimulateResourceDataUseUpdate(resources);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 0,
       1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 0, 1);
 }
 
@@ -287,13 +287,13 @@
                                         true /* is_complete */,
                                         false /* completed_before_fcp */));
 
-  SimulateResourceDataUseUpdate(resources);
-  NavigateToUntrackedUrl();
+  tester()->SimulateResourceDataUseUpdate(resources);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 0,
       1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 0, 1);
 }
 
@@ -320,11 +320,11 @@
                                         true /* is_complete */,
                                         true /* completed_before_fcp */));
 
-  SimulateResourceDataUseUpdate(resources);
-  NavigateToUntrackedUrl();
+  tester()->SimulateResourceDataUseUpdate(resources);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Noncached", 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.SubresourceLoading.LoadedCSSJSBeforeFCP.Cached", 0);
 }
diff --git a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer_unittest.cc
index 1fbd67da..30e450d7 100644
--- a/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer_unittest.cc
@@ -69,10 +69,10 @@
   void SimulatePageLoad(bool is_restore, bool simulate_app_background) {
     is_restore_ = is_restore;
     NavigateAndCommit(GURL(kDefaultTestUrl));
-    SimulateTimingUpdate(timing_);
+    tester()->SimulateTimingUpdate(timing_);
 
     auto resources = GetSampleResourceDataUpdateForTesting(10 * 1024);
-    SimulateResourceDataUseUpdate(resources);
+    tester()->SimulateResourceDataUseUpdate(resources);
     for (const auto& resource : resources) {
       if (resource->is_complete) {
         if (resource->cache_type ==
@@ -85,9 +85,9 @@
 
     if (simulate_app_background) {
       // The histograms should be logged when the app is backgrounded.
-      SimulateAppEnterBackground();
+      tester()->SimulateAppEnterBackground();
     } else {
-      NavigateToUntrackedUrl();
+      tester()->NavigateToUntrackedUrl();
     }
   }
 
@@ -111,24 +111,24 @@
 TEST_F(TabRestorePageLoadMetricsObserverTest, NotRestored) {
   ResetTest();
   SimulatePageLoad(false /* is_restore */, false /* simulate_app_background */);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Network", 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Cache", 0);
-  histogram_tester().ExpectTotalCount(
+  tester()->histogram_tester().ExpectTotalCount(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Total", 0);
 }
 
 TEST_F(TabRestorePageLoadMetricsObserverTest, Restored) {
   ResetTest();
   SimulatePageLoad(true /* is_restore */, false /* simulate_app_background */);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Network",
       static_cast<int>(network_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Cache",
       static_cast<int>(cache_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Total",
       static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
 }
@@ -136,13 +136,13 @@
 TEST_F(TabRestorePageLoadMetricsObserverTest, RestoredAppBackground) {
   ResetTest();
   SimulatePageLoad(true /* is_restore */, true /* simulate_app_background */);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Network",
       static_cast<int>(network_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Cache",
       static_cast<int>(cache_bytes_ / 1024), 1);
-  histogram_tester().ExpectUniqueSample(
+  tester()->histogram_tester().ExpectUniqueSample(
       "PageLoad.Clients.TabRestore.Experimental.Bytes.Total",
       static_cast<int>((network_bytes_ + cache_bytes_) / 1024), 1);
 }
diff --git a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
index 418d49d..83b4bbd 100644
--- a/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc
@@ -33,9 +33,9 @@
 
 TEST_F(ThirdPartyMetricsObserverTest, NoCookiesRead_NoneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, BlockedCookiesRead_NotRecorded) {
@@ -43,14 +43,16 @@
 
   // If there are any blocked_by_policy reads, nothing should be recorded. Even
   // if there are subsequent non-blocked third-party reads.
-  SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
-                      net::CookieList(), true /* blocked_by_policy */);
-  SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
+  tester()->SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                true /* blocked_by_policy */);
+  tester()->SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                false /* blocked_by_policy */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectTotalCount(kReadCookieHistogram, 0);
+  tester()->histogram_tester().ExpectTotalCount(kReadCookieHistogram, 0);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
@@ -59,11 +61,11 @@
 
   GURL url = GURL("data:,Hello%2C%20World!");
   ASSERT_FALSE(url.has_host());
-  SimulateCookiesRead(url, GURL("https://top.com"), net::CookieList(),
-                      false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookiesRead(url, GURL("https://top.com"), net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
@@ -72,48 +74,53 @@
 
   GURL url = GURL("https://127.0.0.1/cookies");
   ASSERT_TRUE(url.has_host());
-  SimulateCookiesRead(url, GURL("https://top.com"), net::CookieList(),
-                      false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookiesRead(url, GURL("https://top.com"), net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, OnlyFirstPartyCookiesRead_NotRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookiesRead(GURL("https://top.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookiesRead(GURL("https://top.com"),
+                                GURL("https://top.com"), net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, OneCookieRead_OneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
        ThreeCookiesReadSameThirdParty_OneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  SimulateCookiesRead(GURL("https://a.com/foo"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  SimulateCookiesRead(GURL("https://sub.a.com/bar"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
+  tester()->SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->SimulateCookiesRead(GURL("https://a.com/foo"),
+                                GURL("https://top.com"), net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->SimulateCookiesRead(GURL("https://sub.a.com/bar"),
+                                GURL("https://top.com"), net::CookieList(),
+                                false /* blocked_by_policy */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
@@ -121,22 +128,25 @@
   NavigateAndCommit(GURL("https://top.com"));
 
   // Simulate third-party cookie reads from two different origins.
-  SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  SimulateCookiesRead(GURL("https://b.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->SimulateCookiesRead(GURL("https://b.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 2, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 2, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, NoCookiesChanged_NoneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, BlockedCookiesChanged_NotRecorded) {
@@ -144,12 +154,14 @@
 
   // If there are any blocked_by_policy writes, nothing should be recorded. Even
   // if there are non-blocked third-party writes.
-  SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), true /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectTotalCount(kWriteCookieHistogram, 0);
+  tester()->SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 true /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectTotalCount(kWriteCookieHistogram, 0);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
@@ -158,10 +170,11 @@
 
   GURL url = GURL("data:,Hello%2C%20World!");
   ASSERT_FALSE(url.has_host());
-  SimulateCookieChange(url, GURL("https://top.com"), net::CanonicalCookie(),
-                       false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
+  tester()->SimulateCookieChange(url, GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
@@ -170,45 +183,50 @@
 
   GURL url = GURL("https://127.0.0.1/cookies");
   ASSERT_TRUE(url.has_host());
-  SimulateCookieChange(url, GURL("https://top.com"), net::CanonicalCookie(),
-                       false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
+  tester()->SimulateCookieChange(url, GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
        OnlyFirstPartyCookiesChanged_NotRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookieChange(GURL("https://top.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookieChange(
+      GURL("https://top.com"), GURL("https://top.com"), net::CanonicalCookie(),
+      false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, OneCookieChanged_OneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
        TwoCookiesChangeSameThirdParty_OneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
@@ -216,51 +234,62 @@
   NavigateAndCommit(GURL("https://top.com"));
 
   // Simulate third-party cookie reads from two different origins.
-  SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  SimulateCookieChange(GURL("https://b.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->SimulateCookieChange(GURL("https://a.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->SimulateCookieChange(GURL("https://b.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 2, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 2, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, ReadAndChangeCookies_BothRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
   // Simulate third-party cookie reads from two different origins.
-  SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
-                      net::CookieList(), false /* blocked_by_policy */);
-  SimulateCookieChange(GURL("https://b.com"), GURL("https://top.com"),
-                       net::CanonicalCookie(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateCookiesRead(GURL("https://a.com"), GURL("https://top.com"),
+                                net::CookieList(),
+                                false /* blocked_by_policy */);
+  tester()->SimulateCookieChange(GURL("https://b.com"), GURL("https://top.com"),
+                                 net::CanonicalCookie(),
+                                 false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
-  histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kReadCookieHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kWriteCookieHistogram, 1, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest, NoDomStorageAccess_NoneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kAccessLocalStorageHistogram, 0, 1);
-  histogram_tester().ExpectUniqueSample(kAccessSessionStorageHistogram, 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kAccessLocalStorageHistogram,
+                                                  0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(
+      kAccessSessionStorageHistogram, 0, 1);
 }
 
 TEST_F(ThirdPartyMetricsObserverTest,
        LocalAndSessionStorageAccess_BothRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           true /* local */, false /* blocked_by_policy */);
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           false /* local */, false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), true /* local */,
+                                     false /* blocked_by_policy */);
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), false /* local */,
+                                     false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(kAccessLocalStorageHistogram, 1, 1);
-  histogram_tester().ExpectUniqueSample(kAccessSessionStorageHistogram, 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(kAccessLocalStorageHistogram,
+                                                  1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(
+      kAccessSessionStorageHistogram, 1, 1);
 }
 
 class ThirdPartyDomStorageAccessMetricsObserverTest
@@ -281,78 +310,93 @@
 
   // If there are any blocked_by_policy access, nothing should be recorded. Even
   // if there are subsequent non-blocked third-party access.
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           IsLocal(), true /* blocked_by_policy */);
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     true /* blocked_by_policy */);
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectTotalCount(DomStorageHistogramName(), 0);
+  tester()->histogram_tester().ExpectTotalCount(DomStorageHistogramName(), 0);
 }
 
 TEST_P(ThirdPartyDomStorageAccessMetricsObserverTest,
        NoRegistrableDomainDomStorageAccess_OneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateDomStorageAccess(GURL("data:,Hello%2C%20World!"),
-                           GURL("https://top.com"), IsLocal(),
-                           false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateDomStorageAccess(GURL("data:,Hello%2C%20World!"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 1,
+                                                  1);
 }
 
 TEST_P(ThirdPartyDomStorageAccessMetricsObserverTest,
        OnlyFirstPartyDomStorageAccess_NotRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateDomStorageAccess(GURL("https://top.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateDomStorageAccess(GURL("https://top.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 0, 1);
+  tester()->histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 0,
+                                                  1);
 }
 
 TEST_P(ThirdPartyDomStorageAccessMetricsObserverTest,
        OneDomStorageAccess_OneRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 1, 1);
+  tester()->histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 1,
+                                                  1);
 }
 
 TEST_P(ThirdPartyDomStorageAccessMetricsObserverTest,
        SameRegistrableDomainDifferentOrigin_TwoRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
-  SimulateDomStorageAccess(GURL("https://sub.a.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
+  tester()->SimulateDomStorageAccess(GURL("https://sub.a.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
 
-  NavigateToUntrackedUrl();
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 2, 1);
+  tester()->histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 2,
+                                                  1);
 }
 
 TEST_P(ThirdPartyDomStorageAccessMetricsObserverTest,
        DomStorageAccessMultipleThirdParties_MultipleRecorded) {
   NavigateAndCommit(GURL("https://top.com"));
 
-  // Simulate third-party DOM storage access from two different origins.
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
-  SimulateDomStorageAccess(GURL("https://a.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
-  SimulateDomStorageAccess(GURL("https://b.com"), GURL("https://top.com"),
-                           IsLocal(), false /* blocked_by_policy */);
-  NavigateToUntrackedUrl();
+  // Simulate third-party DOM storage access from two different
+  // origins.
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
+  tester()->SimulateDomStorageAccess(GURL("https://a.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
+  tester()->SimulateDomStorageAccess(GURL("https://b.com"),
+                                     GURL("https://top.com"), IsLocal(),
+                                     false /* blocked_by_policy */);
+  tester()->NavigateToUntrackedUrl();
 
-  histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 2, 1);
+  tester()->histogram_tester().ExpectUniqueSample(DomStorageHistogramName(), 2,
+                                                  1);
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
index 0e469ba4..3ced9fba 100644
--- a/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
+++ b/chrome/browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc
@@ -98,8 +98,8 @@
 };
 
 TEST_F(UkmPageLoadMetricsObserverTest, NoMetrics) {
-  EXPECT_EQ(0ul, test_ukm_recorder().sources_count());
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().sources_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest, Basic) {
@@ -122,53 +122,54 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kExperimental_InputToNavigationStartName,
         50);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kExperimental_Navigation_UserInitiatedName,
         true);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNavigation_PageTransitionName,
         ui::PAGE_TRANSITION_LINK);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNavigation_PageEndReasonName,
         page_load_metrics::END_CLOSE);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kParseTiming_NavigationToParseStartName,
         100);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kDocumentTiming_NavigationToDOMContentLoadedEventFiredName,
         200);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kPaintTiming_NavigationToFirstPaintName,
         250);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToFirstContentfulPaintName, 300);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kDocumentTiming_NavigationToLoadEventFiredName, 500);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNet_HttpResponseCodeName, 200);
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::
             kExperimental_PaintTiming_NavigationToFirstMeaningfulPaintName));
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -189,34 +190,35 @@
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   // Make sure that only the following metrics are logged. In particular, no
   // paint/document/etc timing metrics should be logged for failed provisional
   // loads.
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNavigation_PageTransitionName,
         ui::PAGE_TRANSITION_LINK);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNavigation_PageEndReasonName,
         page_load_metrics::END_PROVISIONAL_LOAD_FAILED);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kNet_EffectiveConnectionType2_OnNavigationStartName,
         metrics::SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_2G);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNet_ErrorCode_OnFailedProvisionalLoadName,
         static_cast<int64_t>(net::ERR_TIMED_OUT) * -1);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::kPageTiming_NavigationToFailedProvisionalLoadName));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kExperimental_Navigation_UserInitiatedName,
         false);
   }
@@ -231,24 +233,25 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::
             kExperimental_PaintTiming_NavigationToFirstMeaningfulPaintName,
         600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -263,23 +266,24 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestImagePaintName,
         600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -299,15 +303,16 @@
   mock_clock->Advance(base::TimeDelta::FromMilliseconds(1));
   web_contents()->WasHidden();
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   for (const auto& kv : merged_entries) {
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestImagePaintName));
   }
@@ -328,18 +333,19 @@
   timing.paint_timing->largest_image_paint = base::TimeDelta::FromSeconds(10);
   timing.paint_timing->largest_text_paint = base::TimeDelta::FromSeconds(10);
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   for (const auto& kv : merged_entries) {
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestImagePaintName));
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestTextPaintName));
   }
@@ -359,7 +365,7 @@
   timing.paint_timing->largest_text_paint =
       base::TimeDelta::FromMilliseconds(60);
   timing.paint_timing->largest_text_paint_size = 10u;
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   timing.paint_timing->largest_image_paint =
       base::TimeDelta::FromMilliseconds(600);
@@ -367,27 +373,28 @@
   timing.paint_timing->largest_text_paint =
       base::TimeDelta::FromMilliseconds(600);
   timing.paint_timing->largest_text_paint_size = 10u;
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestImagePaintName,
         600);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestTextPaintName,
         600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -402,23 +409,24 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kExperimental_PaintTiming_NavigationToLargestTextPaintName,
         600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -436,7 +444,7 @@
     PopulateRequiredTimingFields(&timing);
 
     NavigateAndCommit(GURL(kTestUrl1));
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
 
     // Simulate closing the tab.
     DeleteContents();
@@ -478,13 +486,13 @@
     PopulateRequiredTimingFields(&timing);
 
     NavigateAndCommit(GURL(kTestUrl1));
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
 
     timing.paint_timing->largest_text_paint = base::Optional<base::TimeDelta>();
     timing.paint_timing->largest_text_paint_size = 0;
     PopulateRequiredTimingFields(&timing);
 
-    SimulateTimingUpdate(timing);
+    tester()->SimulateTimingUpdate(timing);
 
     // Simulate closing the tab.
     DeleteContents();
@@ -516,22 +524,23 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -546,22 +555,23 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -580,22 +590,23 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -624,27 +635,28 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 4780);
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::
             kPaintTiming_NavigationToLargestContentfulPaint_MainFrameName));
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -673,27 +685,28 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 4780);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToLargestContentfulPaint_MainFrameName,
         4780);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -728,23 +741,24 @@
               ->AppendChild("subframe"));
 
   // Simulate timing updates in the main frame and the subframe.
-  SimulateTimingUpdate(timing);
-  SimulateTimingUpdate(subframe_timing, subframe);
+  tester()->SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(subframe_timing, subframe);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kPaintTiming_NavigationToLargestContentfulPaintName, 990);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -758,22 +772,23 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kExperimental_NavigationToInteractiveName,
         600);
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -789,21 +804,22 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kExperimental_NavigationToInteractiveName));
-    EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+    EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(), PageLoad::kPageTiming_ForegroundDurationName));
   }
 }
@@ -819,21 +835,22 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kInteractiveTiming_FirstInputDelay4Name, 50);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kInteractiveTiming_FirstInputTimestamp4Name,
         712);
   }
@@ -850,22 +867,23 @@
   PopulateRequiredTimingFields(&timing);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing);
+  tester()->SimulateTimingUpdate(timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kInteractiveTiming_LongestInputDelay4Name,
         50);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kInteractiveTiming_LongestInputTimestamp4Name, 712);
   }
@@ -886,21 +904,22 @@
   PopulateRequiredTimingFields(&timing2);
 
   NavigateAndCommit(GURL(kTestUrl1));
-  SimulateTimingUpdate(timing1);
+  tester()->SimulateTimingUpdate(timing1);
 
   NavigateAndCommit(GURL(kTestUrl2));
-  SimulateTimingUpdate(timing2);
+  tester()->SimulateTimingUpdate(timing2);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(2ul, merged_entries.size());
   const ukm::mojom::UkmEntry* entry1 = nullptr;
   const ukm::mojom::UkmEntry* entry2 = nullptr;
   for (const auto& kv : merged_entries) {
-    if (test_ukm_recorder().EntryHasMetric(
+    if (tester()->test_ukm_recorder().EntryHasMetric(
             kv.second.get(),
             PageLoad::kPaintTiming_NavigationToFirstContentfulPaintName)) {
       entry1 = kv.second.get();
@@ -911,32 +930,34 @@
   ASSERT_NE(entry1, nullptr);
   ASSERT_NE(entry2, nullptr);
 
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry1, GURL(kTestUrl1));
-  test_ukm_recorder().ExpectEntryMetric(entry1,
-                                        PageLoad::kNavigation_PageEndReasonName,
-                                        page_load_metrics::END_NEW_NAVIGATION);
-  test_ukm_recorder().ExpectEntryMetric(
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry1,
+                                                        GURL(kTestUrl1));
+  tester()->test_ukm_recorder().ExpectEntryMetric(
+      entry1, PageLoad::kNavigation_PageEndReasonName,
+      page_load_metrics::END_NEW_NAVIGATION);
+  tester()->test_ukm_recorder().ExpectEntryMetric(
       entry1, PageLoad::kPaintTiming_NavigationToFirstContentfulPaintName, 200);
-  EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+  EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
       entry1,
       PageLoad::
           kExperimental_PaintTiming_NavigationToFirstMeaningfulPaintName));
-  EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+  EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
       entry1, PageLoad::kPageTiming_ForegroundDurationName));
 
-  test_ukm_recorder().ExpectEntrySourceHasUrl(entry2, GURL(kTestUrl2));
-  test_ukm_recorder().ExpectEntryMetric(entry2,
-                                        PageLoad::kNavigation_PageEndReasonName,
-                                        page_load_metrics::END_CLOSE);
-  EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+  tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(entry2,
+                                                        GURL(kTestUrl2));
+  tester()->test_ukm_recorder().ExpectEntryMetric(
+      entry2, PageLoad::kNavigation_PageEndReasonName,
+      page_load_metrics::END_CLOSE);
+  EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
       entry2, PageLoad::kParseTiming_NavigationToParseStartName));
-  EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+  EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
       entry2, PageLoad::kPaintTiming_NavigationToFirstContentfulPaintName));
-  EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+  EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
       entry2,
       PageLoad::
           kExperimental_PaintTiming_NavigationToFirstMeaningfulPaintName));
-  EXPECT_TRUE(test_ukm_recorder().EntryHasMetric(
+  EXPECT_TRUE(tester()->test_ukm_recorder().EntryHasMetric(
       entry2, PageLoad::kPageTiming_ForegroundDurationName));
 }
 
@@ -956,23 +977,24 @@
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kNet_EffectiveConnectionType2_OnNavigationStartName,
         metrics::SystemProfileProto::Network::EFFECTIVE_CONNECTION_TYPE_3G);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNet_HttpRttEstimate_OnNavigationStartName,
         100);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kNet_TransportRttEstimate_OnNavigationStartName, 200);
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(),
         PageLoad::kNet_DownstreamKbpsEstimate_OnNavigationStartName, 300);
   }
@@ -980,20 +1002,21 @@
 
 TEST_F(UkmPageLoadMetricsObserverTest, PageTransitionReload) {
   GURL url(kTestUrl1);
-  NavigateWithPageTransitionAndCommit(GURL(kTestUrl1),
-                                      ui::PAGE_TRANSITION_RELOAD);
+  tester()->NavigateWithPageTransitionAndCommit(GURL(kTestUrl1),
+                                                ui::PAGE_TRANSITION_RELOAD);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNavigation_PageTransitionName,
         ui::PAGE_TRANSITION_RELOAD);
   }
@@ -1011,7 +1034,7 @@
   resources.push_back(CreateResource(
       false /* was_cached */, 40 * 1024 /* delta_bytes */,
       40 * 1024 /* encoded_body_length */, true /* is_complete */));
-  SimulateResourceDataUseUpdate(resources);
+  tester()->SimulateResourceDataUseUpdate(resources);
 
   int64_t network_bytes = 0;
   int64_t cache_bytes = 0;
@@ -1032,16 +1055,17 @@
   int64_t bucketed_cache_bytes = ukm::GetExponentialBucketMin(cache_bytes, 1.3);
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(kv.second.get(), "Net.NetworkBytes2",
-                                          bucketed_network_bytes);
-    test_ukm_recorder().ExpectEntryMetric(kv.second.get(), "Net.CacheBytes2",
-                                          bucketed_cache_bytes);
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
+        kv.second.get(), "Net.NetworkBytes2", bucketed_network_bytes);
+    tester()->test_ukm_recorder().ExpectEntryMetric(
+        kv.second.get(), "Net.CacheBytes2", bucketed_cache_bytes);
   }
 }
 
@@ -1051,20 +1075,21 @@
   // Simulate some CPU usage.
   page_load_metrics::mojom::CpuTiming cpu_timing(
       base::TimeDelta::FromMilliseconds(500));
-  SimulateCpuTimingUpdate(cpu_timing);
+  tester()->SimulateCpuTimingUpdate(cpu_timing);
 
   // Simulate closing the tab.
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(kv.second.get(),
-                                          PageLoad::kCpuTimeName, 500);
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
+        kv.second.get(), PageLoad::kCpuTimeName, 500);
   }
 }
 
@@ -1072,19 +1097,19 @@
   NavigateAndCommit(GURL(kTestUrl1));
 
   page_load_metrics::mojom::FrameRenderDataUpdate render_data(1.0, 1.0);
-  SimulateRenderDataUpdate(render_data);
+  tester()->SimulateRenderDataUpdate(render_data);
 
   // Simulate hiding the tab (the report should include shifts after hide).
   web_contents()->WasHidden();
 
   render_data.layout_shift_delta = 1.5;
   render_data.layout_shift_delta_before_input_or_scroll = 0.0;
-  SimulateRenderDataUpdate(render_data);
+  tester()->SimulateRenderDataUpdate(render_data);
 
   // Simulate closing the tab.
   DeleteContents();
 
-  const auto& ukm_recorder = test_ukm_recorder();
+  const auto& ukm_recorder = tester()->test_ukm_recorder();
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
       ukm_recorder.GetMergedEntriesByName(PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
@@ -1101,7 +1126,7 @@
         100);
   }
 
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   "PageLoad.LayoutInstability.CumulativeShiftScore"),
               testing::ElementsAre(base::Bucket(25, 1)));
 }
@@ -1115,7 +1140,7 @@
   // Simulate closing the tab.
   DeleteContents();
 
-  EXPECT_EQ(0ul, test_ukm_recorder().entries_count());
+  EXPECT_EQ(0ul, tester()->test_ukm_recorder().entries_count());
 }
 
 TEST_F(UkmPageLoadMetricsObserverTest, LayoutInstabilitySubframeAggregation) {
@@ -1123,7 +1148,7 @@
 
   // Simulate layout instability in the main frame.
   page_load_metrics::mojom::FrameRenderDataUpdate render_data(1.0, 1.0);
-  SimulateRenderDataUpdate(render_data);
+  tester()->SimulateRenderDataUpdate(render_data);
 
   RenderFrameHost* subframe =
       NavigationSimulator::NavigateAndCommitFromDocument(
@@ -1133,22 +1158,22 @@
 
   // Simulate layout instability in the subframe.
   render_data.layout_shift_delta = 1.5;
-  SimulateRenderDataUpdate(render_data, subframe);
+  tester()->SimulateRenderDataUpdate(render_data, subframe);
 
   // Simulate closing the tab.
   DeleteContents();
 
   // CLS score should be the sum of LS scores from all frames.
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   "PageLoad.LayoutInstability.CumulativeShiftScore"),
               testing::ElementsAre(base::Bucket(25, 1)));
 
   // Main-frame (DCLS) score includes only the LS scores in the main frame.
-  EXPECT_THAT(histogram_tester().GetAllSamples(
+  EXPECT_THAT(tester()->histogram_tester().GetAllSamples(
                   "PageLoad.LayoutInstability.CumulativeShiftScore.MainFrame"),
               testing::ElementsAre(base::Bucket(10, 1)));
 
-  const auto& ukm_recorder = test_ukm_recorder();
+  const auto& ukm_recorder = tester()->test_ukm_recorder();
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
       ukm_recorder.GetMergedEntriesByName(PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
@@ -1181,13 +1206,14 @@
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::kThirdPartyCookieBlockingEnabledForSiteName));
   }
@@ -1207,13 +1233,14 @@
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    EXPECT_FALSE(test_ukm_recorder().EntryHasMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    EXPECT_FALSE(tester()->test_ukm_recorder().EntryHasMetric(
         kv.second.get(),
         PageLoad::kThirdPartyCookieBlockingEnabledForSiteName));
   }
@@ -1232,13 +1259,14 @@
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kThirdPartyCookieBlockingEnabledForSiteName,
         true);
   }
@@ -1261,13 +1289,14 @@
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kThirdPartyCookieBlockingEnabledForSiteName,
         false);
   }
@@ -1310,13 +1339,14 @@
   DeleteContents();
 
   std::map<ukm::SourceId, ukm::mojom::UkmEntryPtr> merged_entries =
-      test_ukm_recorder().GetMergedEntriesByName(PageLoad::kEntryName);
+      tester()->test_ukm_recorder().GetMergedEntriesByName(
+          PageLoad::kEntryName);
   EXPECT_EQ(1ul, merged_entries.size());
 
   for (const auto& kv : merged_entries) {
-    test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
-                                                GURL(kTestUrl1));
-    test_ukm_recorder().ExpectEntryMetric(
+    tester()->test_ukm_recorder().ExpectEntrySourceHasUrl(kv.second.get(),
+                                                          GURL(kTestUrl1));
+    tester()->test_ukm_recorder().ExpectEntryMetric(
         kv.second.get(), PageLoad::kNavigation_PageEndReasonName,
         page_load_metrics::END_CLOSE);
   }
diff --git a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
index 0849b67..827562c 100644
--- a/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
+++ b/chrome/browser/password_manager/chrome_password_manager_client_unittest.cc
@@ -148,7 +148,7 @@
         logging_state_active_(false),
         binding_(this) {}
 
-  ~FakePasswordAutofillAgent() override {}
+  ~FakePasswordAutofillAgent() override = default;
 
   void BindRequest(mojo::ScopedInterfaceEndpointHandle handle) {
     binding_.Bind(autofill::mojom::PasswordAutofillAgentAssociatedRequest(
@@ -171,6 +171,8 @@
 
   void FillIntoFocusedField(bool is_password,
                             const base::string16& credential) override {}
+  void AnnotateFieldsWithParsingResult(
+      const autofill::ParsingResult& parsing_result) override {}
 
   void SetLoggingState(bool active) override {
     called_set_logging_state_ = true;
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 3d716ec..d3caa4d 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -39,6 +39,7 @@
 #include "components/autofill/core/browser/autofill_test_utils.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
 #include "components/autofill/core/common/autofill_features.h"
+#include "components/autofill/core/common/autofill_switches.h"
 #include "components/autofill/core/common/password_form.h"
 #include "components/password_manager/content/browser/content_password_manager_driver.h"
 #include "components/password_manager/content/browser/content_password_manager_driver_factory.h"
@@ -66,6 +67,7 @@
 #include "ui/events/keycodes/keyboard_codes.h"
 #include "ui/gfx/geometry/point.h"
 
+using autofill::ParsingResult;
 using base::ASCIIToUTF16;
 using base::Feature;
 using testing::_;
@@ -3887,5 +3889,40 @@
   WaitForElementValue("password_field", "pw");
 }
 
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, ParserAnnotations) {
+  base::CommandLine::ForCurrentProcess()->AppendSwitch(
+      autofill::switches::kShowAutofillSignatures);
+  NavigateToFile("/password/password_form.html");
+  const char kGetAnnotation[] =
+      "window.domAutomationController.send("
+      "  document.getElementById('%s').getAttribute('pm_parser_annotation'));";
+
+  std::string username_annotation;
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      RenderFrameHost(), base::StringPrintf(kGetAnnotation, "username_field"),
+      &username_annotation));
+  EXPECT_EQ("username_element", username_annotation);
+
+  std::string password_annotation;
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      RenderFrameHost(), base::StringPrintf(kGetAnnotation, "password_field"),
+      &password_annotation));
+  EXPECT_EQ("password_element", password_annotation);
+
+  std::string new_password_annotation;
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      RenderFrameHost(),
+      base::StringPrintf(kGetAnnotation, "chg_new_password_1"),
+      &new_password_annotation));
+  EXPECT_EQ("new_password_element", new_password_annotation);
+
+  std::string cofirmation_password_annotation;
+  ASSERT_TRUE(content::ExecuteScriptWithoutUserGestureAndExtractString(
+      RenderFrameHost(),
+      base::StringPrintf(kGetAnnotation, "chg_new_password_2"),
+      &cofirmation_password_annotation));
+  EXPECT_EQ("confirmation_password_element", cofirmation_password_annotation);
+}
+
 }  // namespace
 }  // namespace password_manager
diff --git a/chrome/browser/predictors/loading_predictor_browsertest.cc b/chrome/browser/predictors/loading_predictor_browsertest.cc
index 98dcfa5..bf9c7a4 100644
--- a/chrome/browser/predictors/loading_predictor_browsertest.cc
+++ b/chrome/browser/predictors/loading_predictor_browsertest.cc
@@ -677,7 +677,6 @@
 // Tests that the LoadingPredictor has a prediction for a host after navigating
 // to it.
 IN_PROC_BROWSER_TEST_F(LoadingPredictorBrowserTest, LearnFromNavigation) {
-  base::test::ScopedFeatureList scoped_feature_list;
   GURL url = embedded_test_server()->GetURL(
       "test.com", GetPathWithPortReplacement(kHtmlSubresourcesPath,
                                              embedded_test_server()->port()));
@@ -697,15 +696,22 @@
               testing::UnorderedElementsAreArray(requests));
 }
 
+class LoadingPredictorBrowserTestLearnAllResources
+    : public LoadingPredictorBrowserTest {
+ public:
+  LoadingPredictorBrowserTestLearnAllResources() {
+    feature_list_.InitAndDisableFeature(
+        features::kLoadingOnlyLearnHighPriorityResources);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Tests that the LoadingPredictor has a prediction for a host after navigating
 // to it. Disables kLoadingOnlyLearnHighPriorityResources.
-IN_PROC_BROWSER_TEST_F(
-    LoadingPredictorBrowserTest,
-    LearnFromNavigation_DisablekLoadingOnlyLearnHighPriorityResources) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndDisableFeature(
-      features::kLoadingOnlyLearnHighPriorityResources);
-
+IN_PROC_BROWSER_TEST_F(LoadingPredictorBrowserTestLearnAllResources,
+                       LearnFromNavigation) {
   GURL url = embedded_test_server()->GetURL(
       "test.com", GetPathWithPortReplacement(kHtmlSubresourcesPath,
                                              embedded_test_server()->port()));
diff --git a/chrome/browser/previews/defer_all_script_browsertest.cc b/chrome/browser/previews/defer_all_script_browsertest.cc
index 4afbe02..1d8d203 100644
--- a/chrome/browser/previews/defer_all_script_browsertest.cc
+++ b/chrome/browser/previews/defer_all_script_browsertest.cc
@@ -80,10 +80,7 @@
 class DeferAllScriptBrowserTest : public InProcessBrowserTest,
                                   public testing::WithParamInterface<bool> {
  public:
-  DeferAllScriptBrowserTest() = default;
-  ~DeferAllScriptBrowserTest() override = default;
-
-  void SetUp() override {
+  DeferAllScriptBrowserTest() {
     scoped_feature_list_.InitWithFeatures(
         {previews::features::kPreviews,
          previews::features::kDeferAllScriptPreviews,
@@ -99,10 +96,10 @@
       param_feature_list_.InitWithFeatures(
           {}, {optimization_guide::features::kOptimizationGuideKeyedService});
     }
-
-    InProcessBrowserTest::SetUp();
   }
 
+  ~DeferAllScriptBrowserTest() override = default;
+
   void SetUpOnMainThread() override {
     g_browser_process->network_quality_tracker()
         ->ReportEffectiveConnectionTypeForTesting(
@@ -300,6 +297,14 @@
       &histogram_tester, "Previews.DeferAllScript.DenyListMatch", 1);
   histogram_tester.ExpectUniqueSample("Previews.DeferAllScript.DenyListMatch",
                                       true, 1);
+  // Verify UKM entry.
+  using UkmEntry = ukm::builders::Previews;
+  auto entries = test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName);
+  ASSERT_EQ(1u, entries.size());
+  auto* entry = entries.at(0);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry, UkmEntry::kdefer_all_script_eligibility_reasonName,
+      static_cast<int>(previews::PreviewsEligibilityReason::DENY_LIST_MATCHED));
 
   EXPECT_EQ(kNonDeferredPageExpectedOutput, GetScriptLog());
 }
@@ -333,18 +338,26 @@
   histogram_tester.ExpectTotalCount("Previews.PageEndReason.DeferAllScript", 0);
 }
 
+class DeferAllScriptBrowserTestWithCoinFlipHoldback
+    : public DeferAllScriptBrowserTest {
+ public:
+  DeferAllScriptBrowserTestWithCoinFlipHoldback() {
+    // Holdback the page load from previews and also disable offline previews to
+    // ensure that only post-commit previews are enabled.
+    feature_list_.InitWithFeaturesAndParameters(
+        {{previews::features::kCoinFlipHoldback,
+          {{"force_coin_flip_always_holdback", "true"}}}},
+        {previews::features::kOfflinePreviews});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 IN_PROC_BROWSER_TEST_P(
-    DeferAllScriptBrowserTest,
+    DeferAllScriptBrowserTestWithCoinFlipHoldback,
     DISABLE_ON_WIN_MAC_CHROMESOS(
         DeferAllScriptHttpsWhitelistedButWithCoinFlipHoldback)) {
-  // Holdback the page load from previews and also disable offline previews to
-  // ensure that only post-commit previews are enabled.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeaturesAndParameters(
-      {{previews::features::kCoinFlipHoldback,
-        {{"force_coin_flip_always_holdback", "true"}}}},
-      {previews::features::kOfflinePreviews});
-
   GURL url = https_url();
 
   // Whitelist DeferAllScript for any path for the url's host.
@@ -389,6 +402,10 @@
   }
 }
 
+INSTANTIATE_TEST_SUITE_P(OptimizationGuideKeyedServiceImplementation,
+                         DeferAllScriptBrowserTestWithCoinFlipHoldback,
+                         testing::Bool());
+
 // The client_redirect_url (/client_redirect_base.html) performs a client
 // redirect to "/client_redirect_loop_with_defer_all_script.html" which
 // peforms a client redirect back to the initial client_redirect_url if
@@ -430,6 +447,7 @@
   histogram_tester.ExpectTotalCount(
       "Navigation.ClientRedirectCycle.RedirectToReferrer", 2);
   histogram_tester.ExpectTotalCount("Previews.PageEndReason.DeferAllScript", 3);
+
   EXPECT_FALSE(previews_service->IsUrlEligibleForDeferAllScriptPreview(
       client_redirect_url()));
   EXPECT_FALSE(previews_service->IsUrlEligibleForDeferAllScriptPreview(
@@ -538,4 +556,14 @@
   // preview.
   EXPECT_TRUE(
       previews_service->IsUrlEligibleForDeferAllScriptPreview(https_url()));
+
+  // Verify UKM entry.
+  using UkmEntry = ukm::builders::Previews;
+  auto entries = test_ukm_recorder.GetEntriesByName(UkmEntry::kEntryName);
+  ASSERT_EQ(4u, entries.size());
+  auto* entry = entries.at(3);
+  test_ukm_recorder.ExpectEntryMetric(
+      entry, UkmEntry::kdefer_all_script_eligibility_reasonName,
+      static_cast<int>(
+          previews::PreviewsEligibilityReason::REDIRECT_LOOP_DETECTED));
 }
diff --git a/chrome/browser/previews/hints_fetcher_browsertest.cc b/chrome/browser/previews/hints_fetcher_browsertest.cc
index 93fc9c42..dd5504a 100644
--- a/chrome/browser/previews/hints_fetcher_browsertest.cc
+++ b/chrome/browser/previews/hints_fetcher_browsertest.cc
@@ -416,6 +416,9 @@
     optimization_guide::proto::GetHintsRequest hints_request;
     EXPECT_TRUE(hints_request.ParseFromString(request.content));
     EXPECT_FALSE(hints_request.hosts().empty());
+    EXPECT_GE(optimization_guide::features::
+                  MaxHostsForOptimizationGuideServiceHintsFetch(),
+              static_cast<size_t>(hints_request.hosts().size()));
 
     VerifyHintsMatchExpectedHosts(hints_request);
 
@@ -1325,10 +1328,10 @@
   }
   ASSERT_TRUE(top_host_provider);
 
-  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts(1);
+  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts();
   EXPECT_EQ(0u, top_hosts.size());
 
-  top_hosts = top_host_provider->GetTopHosts(1000);
+  top_hosts = top_host_provider->GetTopHosts();
   EXPECT_EQ(0u, top_hosts.size());
 
   // Everything HTTPS origin within the site engagement service should now be in
diff --git a/chrome/browser/previews/previews_content_util.cc b/chrome/browser/previews/previews_content_util.cc
index 1ac3496..bcfef1b 100644
--- a/chrome/browser/previews/previews_content_util.cc
+++ b/chrome/browser/previews/previews_content_util.cc
@@ -463,6 +463,18 @@
         previews_state &= ~content::DEFER_ALL_SCRIPT_ON;
         UMA_HISTOGRAM_BOOLEAN(
             "Previews.DeferAllScript.RedirectLoopDetectedUsingCache", true);
+        if (previews_service->previews_ui_service()) {
+          previews::PreviewsDeciderImpl* previews_decider_impl =
+              previews_service->previews_ui_service()->previews_decider_impl();
+          DCHECK(previews_decider_impl);
+          std::vector<PreviewsEligibilityReason> passed_reasons;
+          previews_decider_impl->LogPreviewDecisionMade(
+              PreviewsEligibilityReason::REDIRECT_LOOP_DETECTED,
+              navigation_handle->GetURL(),
+              base::DefaultClock::GetInstance()->Now(),
+              PreviewsType::DEFER_ALL_SCRIPT, std::move(passed_reasons),
+              previews_data);
+        }
       }
     }
   }
@@ -478,6 +490,18 @@
           previews_service->MatchesDeferAllScriptDenyListRegexp(url)) {
         previews_state &= ~content::DEFER_ALL_SCRIPT_ON;
         UMA_HISTOGRAM_BOOLEAN("Previews.DeferAllScript.DenyListMatch", true);
+        if (previews_service->previews_ui_service()) {
+          previews::PreviewsDeciderImpl* previews_decider_impl =
+              previews_service->previews_ui_service()->previews_decider_impl();
+          DCHECK(previews_decider_impl);
+          std::vector<PreviewsEligibilityReason> passed_reasons;
+          previews_decider_impl->LogPreviewDecisionMade(
+              PreviewsEligibilityReason::DENY_LIST_MATCHED,
+              navigation_handle->GetURL(),
+              base::DefaultClock::GetInstance()->Now(),
+              PreviewsType::DEFER_ALL_SCRIPT, std::move(passed_reasons),
+              previews_data);
+        }
       }
     }
   }
diff --git a/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc b/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc
index 8c76ad3..f78523ad 100644
--- a/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc
+++ b/chrome/browser/previews/previews_lite_page_redirect_browsertest.cc
@@ -16,6 +16,7 @@
 #include "base/files/scoped_temp_dir.h"
 #include "base/metrics/field_trial_param_associator.h"
 #include "base/metrics/field_trial_params.h"
+#include "base/optional.h"
 #include "base/rand_util.h"
 #include "base/run_loop.h"
 #include "base/strings/string_number_conversions.h"
@@ -304,10 +305,12 @@
         {"origin_probe_timeout_ms", "500"},
     };
 
-    scoped_parameterized_feature_list_.InitAndEnableFeatureWithParameters(
+    scoped_parameterized_feature_list_.emplace();
+    scoped_parameterized_feature_list_->InitAndEnableFeatureWithParameters(
         previews::features::kLitePageServerPreviews, feature_parameters);
 
-    scoped_feature_list_.InitWithFeatures(
+    scoped_feature_list_.emplace();
+    scoped_feature_list_->InitWithFeatures(
         {previews::features::kPreviews,
          optimization_guide::features::kOptimizationHints,
          previews::features::kResourceLoadingHints,
@@ -316,19 +319,28 @@
          network::features::kReporting},
         {network::features::kNetworkErrorLogging});
 
+    opt_guide_keyed_service_feature_list_.emplace();
     if (UseOptimizationGuideKeyedServiceImplementation()) {
-      opt_guide_keyed_service_feature_list_.InitWithFeatures(
+      opt_guide_keyed_service_feature_list_->InitWithFeatures(
           {optimization_guide::features::kOptimizationGuideKeyedService}, {});
     } else {
-      opt_guide_keyed_service_feature_list_.InitWithFeatures(
+      opt_guide_keyed_service_feature_list_->InitWithFeatures(
           {}, {optimization_guide::features::kOptimizationGuideKeyedService});
     }
 
-    drp_holdback_feature_list_.InitWithFeatureState(
+    drp_holdback_feature_list_.emplace();
+    drp_holdback_feature_list_->InitWithFeatureState(
         data_reduction_proxy::features::kDataReductionProxyHoldback,
         ShouldEnableDRPHoldback());
   }
 
+  void TearDown() override {
+    drp_holdback_feature_list_.reset();
+    opt_guide_keyed_service_feature_list_.reset();
+    scoped_feature_list_.reset();
+    scoped_parameterized_feature_list_.reset();
+  }
+
   void SetUpOnMainThread() override {
     InProcessBrowserTest::SetUpOnMainThread();
     InitializeOptimizationHints();
@@ -897,11 +909,12 @@
     previews_server_connections_.insert(unique_socket_id);
   }
 
-  base::test::ScopedFeatureList scoped_parameterized_feature_list_;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  base::test::ScopedFeatureList url_loader_feature_list_;
-  base::test::ScopedFeatureList opt_guide_keyed_service_feature_list_;
-  base::test::ScopedFeatureList drp_holdback_feature_list_;
+  base::Optional<base::test::ScopedFeatureList>
+      scoped_parameterized_feature_list_;
+  base::Optional<base::test::ScopedFeatureList> scoped_feature_list_;
+  base::Optional<base::test::ScopedFeatureList>
+      opt_guide_keyed_service_feature_list_;
+  base::Optional<base::test::ScopedFeatureList> drp_holdback_feature_list_;
   std::unique_ptr<net::EmbeddedTestServer> previews_server_;
   std::unique_ptr<net::EmbeddedTestServer> https_server_;
   std::unique_ptr<net::EmbeddedTestServer> http_server_;
@@ -1212,14 +1225,22 @@
       1);
 }
 
-IN_PROC_BROWSER_TEST_P(
-    PreviewsLitePageRedirectServerBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMESOS(CoinFlipHoldbackTriggering)) {
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      {previews::features::kCoinFlipHoldback},
-      {{"force_coin_flip_always_holdback", "true"}});
+class PreviewsLitePageRedirectServerBrowserTestWithAlwaysHoldback
+    : public PreviewsLitePageRedirectServerBrowserTest {
+ public:
+  PreviewsLitePageRedirectServerBrowserTestWithAlwaysHoldback() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        {previews::features::kCoinFlipHoldback},
+        {{"force_coin_flip_always_holdback", "true"}});
+  }
 
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_P(
+    PreviewsLitePageRedirectServerBrowserTestWithAlwaysHoldback,
+    DISABLE_ON_WIN_MAC_CHROMESOS(CoinFlipHoldbackTriggering)) {
   ui_test_utils::NavigateToURL(browser(), HttpsLitePageURL(kSuccess));
   VerifyPreviewNotLoaded();
 }
@@ -1923,8 +1944,6 @@
       public testing::WithParamInterface<NetworkIsolationKeyMode> {
  public:
   void SetUp() override {
-    BasePreviewsLitePageRedirectServerBrowserTest::SetUp();
-
     switch (GetParam()) {
       case NetworkIsolationKeyMode::kNone:
         break;
@@ -1950,6 +1969,8 @@
             {});
         break;
     }
+
+    BasePreviewsLitePageRedirectServerBrowserTest::SetUp();
   }
 
   bool UseOptimizationGuideKeyedServiceImplementation() const override {
@@ -2106,13 +2127,14 @@
 class CoinFlipHoldbackExperimentBrowserTest
     : public PreviewsLitePageRedirectAndPageHintsBrowserTest {
  public:
-  CoinFlipHoldbackExperimentBrowserTest() = default;
+  CoinFlipHoldbackExperimentBrowserTest() {
+    ukm_feature_list_.InitAndEnableFeature(ukm::kUkmFeature);
+  }
 
   ~CoinFlipHoldbackExperimentBrowserTest() override = default;
 
   void SetUp() override {
     PreviewsLitePageRedirectAndPageHintsBrowserTest::SetUp();
-    ukm_feature_list_.InitAndEnableFeature(ukm::kUkmFeature);
   }
 
   void SetUpCommandLine(base::CommandLine* cmd) override {
@@ -2125,9 +2147,6 @@
     ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
   }
 
-  // |coin_flip_holdback_enabled|: Whether the coin flip holdback feature flag
-  // should be enabled.
-  //
   // |redirect_navigation|: Whether a preview should only be eligible on a
   // redirect.
   //
@@ -2136,28 +2155,11 @@
   //
   // |allow_resource_loading|: Whether a resource loading preview should be
   // eligible.
-  //
-  // |set_random_navigation_coin_flip|: What the random coin flip should be.
-  // True is holdback, false is allowed.
-  void RunTest(bool coin_flip_holdback_enabled,
-               bool redirect_navigation,
+  void RunTest(bool redirect_navigation,
                bool allow_lite_page_redirect,
-               bool allow_resource_loading,
-               bool set_random_navigation_coin_flip) {
+               bool allow_resource_loading) {
     ukm::InitializeSourceUrlRecorderForWebContents(GetWebContents());
 
-    if (coin_flip_holdback_enabled) {
-      scoped_feature_list_.InitAndEnableFeatureWithParameters(
-          previews::features::kCoinFlipHoldback,
-          {{"force_coin_flip_always_holdback",
-            set_random_navigation_coin_flip ? "true" : "false"},
-           {"force_coin_flip_always_allow",
-            !set_random_navigation_coin_flip ? "true" : "false"}});
-    } else {
-      scoped_feature_list_.InitAndDisableFeature(
-          previews::features::kCoinFlipHoldback);
-    }
-
     GURL final_url = blacklisted_https_url();
     GURL starting_url = redirect_navigation
                             ? HttpsLitePageURL(kRedirectNonPreview)
@@ -2253,10 +2255,48 @@
 
  private:
   std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
-  base::test::ScopedFeatureList scoped_feature_list_;
   base::test::ScopedFeatureList ukm_feature_list_;
 };
 
+class CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip
+    : public CoinFlipHoldbackExperimentBrowserTest {
+ public:
+  CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        previews::features::kCoinFlipHoldback,
+        {{"force_coin_flip_always_holdback", "true"},
+         {"force_coin_flip_always_allow", "false"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+class CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip
+    : public CoinFlipHoldbackExperimentBrowserTest {
+ public:
+  CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        previews::features::kCoinFlipHoldback,
+        {{"force_coin_flip_always_holdback", "false"},
+         {"force_coin_flip_always_allow", "true"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+class CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled
+    : public CoinFlipHoldbackExperimentBrowserTest {
+ public:
+  CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled() {
+    feature_list_.InitAndDisableFeature(previews::features::kCoinFlipHoldback);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // First param is true if testing using the OptimizationGuideKeyedService
 // implementation. Second param is true if DRP holdback should be enabled.
 INSTANTIATE_TEST_SUITE_P(
@@ -2264,17 +2304,31 @@
     CoinFlipHoldbackExperimentBrowserTest,
     ::testing::Combine(::testing::Bool(), ::testing::Bool()));
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(NoPreviews_NoCoinFlip)) {
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
+INSTANTIATE_TEST_SUITE_P(
+    /* no prefix */,
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
+    ::testing::Combine(::testing::Bool(), ::testing::Bool()));
+
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
+    DISABLE_ON_WIN_MAC_CHROMESOS(NoPreviews_NoCoinFlip)) {
   // Set ECT so that we are sure to not trigger any preview.
   g_browser_process->network_quality_tracker()
       ->ReportEffectiveConnectionTypeForTesting(
           net::EFFECTIVE_CONNECTION_TYPE_4G);
 
-  RunTest(false /* coin_flip_holdback_enabled */,
-          false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2283,12 +2337,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
     DISABLE_ON_WIN_MAC_CHROMESOS(BothPreviewsAllowedWantLPR_NoCoinFlip)) {
-  RunTest(false /* coin_flip_holdback_enabled */,
-          false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
-          true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2296,12 +2348,11 @@
                  true /* want_ukm_previews_likely */);
 }
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(LPRAllowed_NoCoinFlip)) {
-  RunTest(false /* coin_flip_holdback_enabled */,
-          false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
+    DISABLE_ON_WIN_MAC_CHROMESOS(LPRAllowed_NoCoinFlip)) {
+  RunTest(false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2309,12 +2360,11 @@
                  true /* want_ukm_previews_likely */);
 }
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(RLHAllowed_NoCoinFlip)) {
-  RunTest(false /* coin_flip_holdback_enabled */,
-          false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
-          true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
+    DISABLE_ON_WIN_MAC_CHROMESOS(RLHAllowed_NoCoinFlip)) {
+  RunTest(false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  true /* want_resource_loading_committed */,
@@ -2323,17 +2373,15 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
     DISABLE_ON_WIN_MAC_CHROMESOS(NoPreviews_WithRedirect_NoCoinFlip)) {
   // Set ECT so that we are sure to not trigger any preview.
   g_browser_process->network_quality_tracker()
       ->ReportEffectiveConnectionTypeForTesting(
           net::EFFECTIVE_CONNECTION_TYPE_4G);
 
-  RunTest(false /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(true /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2342,12 +2390,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
     DISABLE_ON_WIN_MAC_CHROMESOS(
         BothPreviewsAllowedWantLPR_WithRedirect_NoCoinFlip)) {
-  RunTest(false /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(true /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2356,12 +2403,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
     DISABLE_ON_WIN_MAC_CHROMESOS(LPRAllowed_WithRedirect_NoCoinFlip)) {
-  RunTest(false /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading */,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(true /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading */);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2370,11 +2415,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithCoinFlipHoldbackDisabled,
     DISABLE_ON_WIN_MAC_CHROMESOS(RLHAllowed_WithRedirect_NoCoinFlip)) {
-  RunTest(false /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(true /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  true /* want_resource_loading_committed */,
@@ -2383,17 +2427,15 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(NoPreviews_CoinFlipEnabled_Allowed)) {
   // Set ECT so that we are sure to not trigger any preview.
   g_browser_process->network_quality_tracker()
       ->ReportEffectiveConnectionTypeForTesting(
           net::EFFECTIVE_CONNECTION_TYPE_4G);
 
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2402,12 +2444,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(
         BothPreviewsAllowedWantLPR_CoinFlipEnabled_Allowed)) {
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2416,11 +2457,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(LPRAllowed_CoinFlipEnabled_Allowed)) {
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/, false /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2429,11 +2469,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(RLHAllowed_CoinFlipEnabled_Allowed)) {
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  true /* want_resource_loading_committed */,
@@ -2441,18 +2480,17 @@
                  true /* want_ukm_previews_likely */);
 }
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(
-                           NoPreviews_WithRedirect_CoinFlipEnabled_Allowed)) {
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
+    DISABLE_ON_WIN_MAC_CHROMESOS(
+        NoPreviews_WithRedirect_CoinFlipEnabled_Allowed)) {
   // Set ECT so that we are sure to not trigger any preview.
   g_browser_process->network_quality_tracker()
       ->ReportEffectiveConnectionTypeForTesting(
           net::EFFECTIVE_CONNECTION_TYPE_4G);
 
-  RunTest(true /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(true /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2461,12 +2499,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(
         BothPreviewsAllowedWantLPR_WithRedirect_CoinFlipEnabled_Allowed)) {
-  RunTest(true /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+  RunTest(true /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2474,13 +2511,12 @@
                  true /* want_ukm_previews_likely */);
 }
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(
-                           LPRAllowed_WithRedirect_CoinFlipEnabled_Allowed)) {
-  RunTest(true /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading */,
-          false /* set_random_navigation_coin_flip*/);
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
+    DISABLE_ON_WIN_MAC_CHROMESOS(
+        LPRAllowed_WithRedirect_CoinFlipEnabled_Allowed)) {
+  RunTest(true /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading */);
 
   ValidateResult(true /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2488,12 +2524,12 @@
                  true /* want_ukm_previews_likely */);
 }
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(
-                           RLHAllowed_WithRedirect_CoinFlipEnabled_Allowed)) {
-  RunTest(true /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          false /* set_random_navigation_coin_flip*/);
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithoutRandomNavigationCoinFlip,
+    DISABLE_ON_WIN_MAC_CHROMESOS(
+        RLHAllowed_WithRedirect_CoinFlipEnabled_Allowed)) {
+  RunTest(true /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  true /* want_resource_loading_committed */,
@@ -2502,17 +2538,15 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(NoPreviews_CoinFlipEnabled_Holdback)) {
   // Set ECT so that we are sure to not trigger any preview.
   g_browser_process->network_quality_tracker()
       ->ReportEffectiveConnectionTypeForTesting(
           net::EFFECTIVE_CONNECTION_TYPE_4G);
 
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading*/,
-          true /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2521,12 +2555,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(
         BothPreviewsAllowedWantLPR_CoinFlipEnabled_Holdback)) {
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          true /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2535,11 +2568,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(LPRAllowed_CoinFlipEnabled_Holdback)) {
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/, false /* allow_resource_loading*/,
-          true /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2548,11 +2580,10 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(RLHAllowed_CoinFlipEnabled_Holdback)) {
-  RunTest(true /* coin_flip_holdback_enabled */, false /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          true /* set_random_navigation_coin_flip*/);
+  RunTest(false /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2561,12 +2592,11 @@
 }
 
 IN_PROC_BROWSER_TEST_P(
-    CoinFlipHoldbackExperimentBrowserTest,
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
     DISABLE_ON_WIN_MAC_CHROMESOS(
         BothPreviewsAllowedWantLPR_WithRedirect_CoinFlipEnabled_Holdback)) {
-  RunTest(true /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          true /* set_random_navigation_coin_flip*/);
+  RunTest(true /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2574,13 +2604,12 @@
                  true /* want_ukm_previews_likely */);
 }
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(
-                           LPRAllowed_WithRedirect_CoinFlipEnabled_Holdback)) {
-  RunTest(true /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          true /* allow_lite_page_redirect*/,
-          false /* allow_resource_loading */,
-          true /* set_random_navigation_coin_flip*/);
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
+    DISABLE_ON_WIN_MAC_CHROMESOS(
+        LPRAllowed_WithRedirect_CoinFlipEnabled_Holdback)) {
+  RunTest(true /* redirect_navigation*/, true /* allow_lite_page_redirect*/,
+          false /* allow_resource_loading */);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
@@ -2588,12 +2617,12 @@
                  true /* want_ukm_previews_likely */);
 }
 
-IN_PROC_BROWSER_TEST_P(CoinFlipHoldbackExperimentBrowserTest,
-                       DISABLE_ON_WIN_MAC_CHROMESOS(
-                           RLHAllowed_WithRedirect_CoinFlipEnabled_Holdback)) {
-  RunTest(true /* coin_flip_holdback_enabled */, true /* redirect_navigation*/,
-          false /* allow_lite_page_redirect*/, true /* allow_resource_loading*/,
-          true /* set_random_navigation_coin_flip*/);
+IN_PROC_BROWSER_TEST_P(
+    CoinFlipHoldbackExperimentBrowserTestWithRandomNavigationCoinFlip,
+    DISABLE_ON_WIN_MAC_CHROMESOS(
+        RLHAllowed_WithRedirect_CoinFlipEnabled_Holdback)) {
+  RunTest(true /* redirect_navigation*/, false /* allow_lite_page_redirect*/,
+          true /* allow_resource_loading*/);
 
   ValidateResult(false /* want_lite_page_redirect_committed */,
                  false /* want_resource_loading_committed */,
diff --git a/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc b/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
index 557b569..60d3264 100644
--- a/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
+++ b/chrome/browser/previews/resource_loading_hints/resource_loading_hints_browsertest.cc
@@ -652,17 +652,24 @@
       "ResourceLoadingHints.CountBlockedSubresourcePatterns", 0);
 }
 
+class ResourceLoadingHintsBrowserTestWithExperimentEnabled
+    : public ResourceLoadingHintsBrowserTest {
+ public:
+  ResourceLoadingHintsBrowserTestWithExperimentEnabled() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        optimization_guide::features::kOptimizationHintsExperiments,
+        {{optimization_guide::features::kOptimizationHintsExperimentNameParam,
+          optimization_guide::testing::kFooExperimentName}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Sets only the experimental hints, and enables the matching experiment.
 // Verifies that the hints are used, and the resource loading is blocked.
-IN_PROC_BROWSER_TEST_P(
-    ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMESOS(ExperimentalHints_ExperimentIsEnabled)) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeatureWithParameters(
-      optimization_guide::features::kOptimizationHintsExperiments,
-      {{optimization_guide::features::kOptimizationHintsExperimentNameParam,
-        optimization_guide::testing::kFooExperimentName}});
-
+IN_PROC_BROWSER_TEST_P(ResourceLoadingHintsBrowserTestWithExperimentEnabled,
+                       DISABLE_ON_WIN_MAC_CHROMESOS(ExperimentalHints)) {
   GURL url = https_url();
 
   // Whitelist resource loading hints for https_hint_setup_url()'s' host.
@@ -696,15 +703,8 @@
 // Sets both the experimental and default hints, and enables the matching
 // experiment. Verifies that the hints are used, and the resource loading is
 // blocked.
-IN_PROC_BROWSER_TEST_P(
-    ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMESOS(MixExperimentalHints_ExperimentIsEnabled)) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeatureWithParameters(
-      optimization_guide::features::kOptimizationHintsExperiments,
-      {{optimization_guide::features::kOptimizationHintsExperimentNameParam,
-        optimization_guide::testing::kFooExperimentName}});
-
+IN_PROC_BROWSER_TEST_P(ResourceLoadingHintsBrowserTestWithExperimentEnabled,
+                       DISABLE_ON_WIN_MAC_CHROMESOS(MixExperimentalHints)) {
   GURL url = https_url();
 
   // Whitelist resource loading hints for https_hint_setup_url()'s' host. Set
@@ -831,18 +831,25 @@
       "ResourceLoadingHints.CountBlockedSubresourcePatterns", 0);
 }
 
+class ResourceLoadingHintsBrowserTestWithExperimentDisabled
+    : public ResourceLoadingHintsBrowserTest {
+ public:
+  ResourceLoadingHintsBrowserTestWithExperimentDisabled() {
+    feature_list_.InitAndEnableFeatureWithParameters(
+        optimization_guide::features::kOptimizationHintsExperiments,
+        {{optimization_guide::features::kOptimizationHintsExperimentNameParam,
+          "some_other_experiment"}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Sets both the experimental and default hints, but does not enable the
 // matching experiment. Verifies that the hints from the experiment are not
 // used.
-IN_PROC_BROWSER_TEST_P(
-    ResourceLoadingHintsBrowserTest,
-    DISABLE_ON_WIN_MAC_CHROMESOS(MixExperimentalHints_ExperimentIsNotEnabled)) {
-  base::test::ScopedFeatureList scoped_list;
-  scoped_list.InitAndEnableFeatureWithParameters(
-      optimization_guide::features::kOptimizationHintsExperiments,
-      {{optimization_guide::features::kOptimizationHintsExperimentNameParam,
-        "some_other_experiment"}});
-
+IN_PROC_BROWSER_TEST_P(ResourceLoadingHintsBrowserTestWithExperimentDisabled,
+                       DISABLE_ON_WIN_MAC_CHROMESOS(MixExperimentalHints)) {
   GURL url = https_url();
 
   // Whitelist resource loading hints for https_hint_setup_url()'s' host.
@@ -978,18 +985,26 @@
       "ResourceLoadingHints.CountBlockedSubresourcePatterns", 0);
 }
 
+class ResourceLoadingHintsBrowserTestWithCoinFlipAlwaysHoldback
+    : public ResourceLoadingHintsBrowserTest {
+ public:
+  ResourceLoadingHintsBrowserTestWithCoinFlipAlwaysHoldback() {
+    // Holdback the page load from previews and also disable offline previews to
+    // ensure that only post-commit previews are enabled.
+    feature_list_.InitWithFeaturesAndParameters(
+        {{previews::features::kCoinFlipHoldback,
+          {{"force_coin_flip_always_holdback", "true"}}}},
+        {previews::features::kOfflinePreviews});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 IN_PROC_BROWSER_TEST_P(
-    ResourceLoadingHintsBrowserTest,
+    ResourceLoadingHintsBrowserTestWithCoinFlipAlwaysHoldback,
     DISABLE_ON_WIN_MAC_CHROMESOS(
         ResourceLoadingHintsHttpsWhitelistedButShouldNotApplyBecauseCoinFlipHoldback)) {
-  // Holdback the page load from previews and also disable offline previews to
-  // ensure that only post-commit previews are enabled.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitWithFeaturesAndParameters(
-      {{previews::features::kCoinFlipHoldback,
-        {{"force_coin_flip_always_holdback", "true"}}}},
-      {previews::features::kOfflinePreviews});
-
   ukm::TestAutoSetUkmRecorder test_ukm_recorder;
 
   GURL url = https_url();
diff --git a/chrome/browser/printing/print_test_utils.cc b/chrome/browser/printing/print_test_utils.cc
index 03ae5d8..3dc5387d 100644
--- a/chrome/browser/printing/print_test_utils.cc
+++ b/chrome/browser/printing/print_test_utils.cc
@@ -17,10 +17,6 @@
 const char kDummyPrinterName[] = "DefaultPrinter";
 
 base::Value GetPrintTicket(PrinterType type) {
-  bool is_cloud_printer = type == kCloudPrinter;
-  bool is_privet_printer = type == kPrivetPrinter;
-  bool is_extension_printer = type == kExtensionPrinter;
-
   base::Value ticket(base::Value::Type::DICTIONARY);
 
   // Letter
@@ -42,10 +38,7 @@
   ticket.SetBoolKey(kSettingShouldPrintSelectionOnly, false);
   ticket.SetBoolKey(kSettingPreviewModifiable, true);
   ticket.SetBoolKey(kSettingPreviewIsPdf, false);
-  ticket.SetBoolKey(kSettingPrintToPDF, type == kPdfPrinter);
-  ticket.SetBoolKey(kSettingCloudPrintDialog, is_cloud_printer);
-  ticket.SetBoolKey(kSettingPrintWithPrivet, is_privet_printer);
-  ticket.SetBoolKey(kSettingPrintWithExtension, is_extension_printer);
+  ticket.SetIntKey(kSettingPrinterType, type);
   ticket.SetBoolKey(kSettingRasterizePdf, false);
   ticket.SetIntKey(kSettingScaleFactor, 100);
   ticket.SetIntKey(kSettingPagesPerSheet, 1);
@@ -57,10 +50,9 @@
   ticket.SetIntKey(kSettingPageHeight, 279400);
   ticket.SetBoolKey(kSettingShowSystemDialog, false);
 
-  if (is_cloud_printer)
+  if (type == kCloudPrinter) {
     ticket.SetStringKey(kSettingCloudPrintId, kDummyPrinterName);
-
-  if (is_privet_printer || is_extension_printer) {
+  } else if (type == kPrivetPrinter || type == kExtensionPrinter) {
     base::Value capabilities(base::Value::Type::DICTIONARY);
     capabilities.SetBoolKey("duplex", true);  // non-empty
     std::string caps_string;
diff --git a/chrome/browser/printing/print_view_manager_basic_unittest.cc b/chrome/browser/printing/print_view_manager_basic_unittest.cc
index 72cfcd7..ba2a2670 100644
--- a/chrome/browser/printing/print_view_manager_basic_unittest.cc
+++ b/chrome/browser/printing/print_view_manager_basic_unittest.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/printing/print_view_manager_basic.h"
 
+#include <utility>
+
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/printing/print_job_manager.h"
 #include "chrome/browser/printing/print_test_utils.h"
diff --git a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
index ebc819a..5d06cda7 100644
--- a/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
+++ b/chrome/browser/resources/chromeos/internet_detail_dialog/internet_detail_dialog.js
@@ -231,7 +231,7 @@
    * @private
    */
   getDefaultConfigProperties_: function() {
-    return {type: this.managedProperties_.type};
+    return OncMojo.getDefaultConfigProperties(this.managedProperties_.type);
   },
 
   /**
@@ -489,7 +489,7 @@
     }
     const config = this.getDefaultConfigProperties_();
     const apn = event.detail;
-    config.cellular = {apn: apn};
+    config.typeConfig.cellular = {apn: apn};
     this.setMojoNetworkProperties_(config);
   },
 
diff --git a/chrome/browser/resources/pdf/viewport.js b/chrome/browser/resources/pdf/viewport.js
index 1a50077..17986ae5 100644
--- a/chrome/browser/resources/pdf/viewport.js
+++ b/chrome/browser/resources/pdf/viewport.js
@@ -621,7 +621,7 @@
       const bottom =
           this.pageDimensions_[page].y + this.pageDimensions_[page].height;
 
-      if (top <= y && bottom > y) {
+      if (top <= y && y <= bottom) {
         return page;
       }
 
diff --git a/chrome/browser/resources/print_preview/BUILD.gn b/chrome/browser/resources/print_preview/BUILD.gn
index 1c68fc0..0bb1822b 100644
--- a/chrome/browser/resources/print_preview/BUILD.gn
+++ b/chrome/browser/resources/print_preview/BUILD.gn
@@ -127,6 +127,7 @@
 js_library("native_layer") {
   deps = [
     "data:destination",
+    "data:destination_match",
     "data:measurement_system",
     "//ui/webui/resources/js:cr",
   ]
diff --git a/chrome/browser/resources/print_preview/data/BUILD.gn b/chrome/browser/resources/print_preview/data/BUILD.gn
index 5ab503b..7f04cc61 100644
--- a/chrome/browser/resources/print_preview/data/BUILD.gn
+++ b/chrome/browser/resources/print_preview/data/BUILD.gn
@@ -19,6 +19,7 @@
     ":measurement_system",
     ":model",
     ":printable_area",
+    ":scaling",
     ":size",
     ":state",
   ]
@@ -50,6 +51,7 @@
 js_library("local_parsers") {
   deps = [
     ":destination",
+    ":destination_match",
     "..:native_layer",
     "//ui/webui/resources/js:cr",
   ]
@@ -58,7 +60,6 @@
 js_library("destination_match") {
   deps = [
     ":destination",
-    "..:native_layer",
     "//ui/webui/resources/js:cr",
   ]
 }
@@ -111,8 +112,10 @@
 js_library("model") {
   deps = [
     ":destination",
+    ":destination_match",
     ":document_info",
     ":margins",
+    ":scaling",
     "//ui/webui/resources/js:cr",
     "//ui/webui/resources/js:promise_resolver",
   ]
@@ -126,6 +129,12 @@
   ]
 }
 
+js_library("scaling") {
+  deps = [
+    "//ui/webui/resources/js:cr",
+  ]
+}
+
 js_library("size") {
   deps = [
     "//ui/webui/resources/js:cr",
diff --git a/chrome/browser/resources/print_preview/data/destination_match.html b/chrome/browser/resources/print_preview/data/destination_match.html
index 35c9a62f..288ce95 100644
--- a/chrome/browser/resources/print_preview/data/destination_match.html
+++ b/chrome/browser/resources/print_preview/data/destination_match.html
@@ -1,5 +1,4 @@
 <link rel="import" href="chrome://resources/html/cr.html">
-<link rel="import" href="../native_layer.html">
 <link rel="import" href="destination.html">
 
 <script src="destination_match.js"></script>
diff --git a/chrome/browser/resources/print_preview/data/destination_match.js b/chrome/browser/resources/print_preview/data/destination_match.js
index 9e3e930..c147e00 100644
--- a/chrome/browser/resources/print_preview/data/destination_match.js
+++ b/chrome/browser/resources/print_preview/data/destination_match.js
@@ -5,6 +5,19 @@
 cr.define('print_preview', function() {
   'use strict';
   /**
+   * Printer types for capabilities and printer list requests.
+   * Must match PrinterType in printing/print_job_constants.h
+   * @enum {number}
+   */
+  const PrinterType = {
+    PRIVET_PRINTER: 0,
+    EXTENSION_PRINTER: 1,
+    PDF_PRINTER: 2,
+    LOCAL_PRINTER: 3,
+    CLOUD_PRINTER: 4
+  };
+
+  /**
    * Converts DestinationOrigin to PrinterType.
    * @param {!print_preview.DestinationOrigin} origin The printer's
    *     destination origin.
@@ -25,6 +38,19 @@
     return print_preview.PrinterType.CLOUD_PRINTER;
   };
 
+  /**
+   * @param {!print_preview.Destination} destination The destination to figure
+   *     out the printer type of.
+   * @return {!print_preview.PrinterType} Map the destination to a PrinterType.
+   */
+  function getPrinterTypeForDestination(destination) {
+    if (destination.id ==
+        print_preview.Destination.GooglePromotedId.SAVE_AS_PDF) {
+      return print_preview.PrinterType.PDF_PRINTER;
+    }
+    return print_preview.originToType(destination.origin);
+  }
+
   class DestinationMatch {
     /**
      * A set of key parameters describing a destination used to determine
@@ -116,5 +142,10 @@
   }
 
   // Export
-  return {originToType: originToType, DestinationMatch: DestinationMatch};
+  return {
+    DestinationMatch: DestinationMatch,
+    PrinterType: PrinterType,
+    getPrinterTypeForDestination: getPrinterTypeForDestination,
+    originToType: originToType,
+  };
 });
diff --git a/chrome/browser/resources/print_preview/data/local_parsers.html b/chrome/browser/resources/print_preview/data/local_parsers.html
index b6fe90e..fed4e851 100644
--- a/chrome/browser/resources/print_preview/data/local_parsers.html
+++ b/chrome/browser/resources/print_preview/data/local_parsers.html
@@ -1,5 +1,6 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="../native_layer.html">
 <link rel="import" href="destination.html">
+<link rel="import" href="destination_match.html">
 
 <script src="local_parsers.js"></script>
diff --git a/chrome/browser/resources/print_preview/data/model.html b/chrome/browser/resources/print_preview/data/model.html
index c29628b..1fb0c92 100644
--- a/chrome/browser/resources/print_preview/data/model.html
+++ b/chrome/browser/resources/print_preview/data/model.html
@@ -3,7 +3,9 @@
 <link rel="import" href="chrome://resources/html/cr.html">
 <link rel="import" href="chrome://resources/html/promise_resolver.html">
 <link rel="import" href="destination.html">
+<link rel="import" href="destination_match.html">
 <link rel="import" href="document_info.html">
 <link rel="import" href="margins.html">
+<link rel="import" href="scaling.html">
 
 <script src="model.js"></script>
diff --git a/chrome/browser/resources/print_preview/data/model.js b/chrome/browser/resources/print_preview/data/model.js
index 008f357f..1d71350 100644
--- a/chrome/browser/resources/print_preview/data/model.js
+++ b/chrome/browser/resources/print_preview/data/model.js
@@ -30,8 +30,9 @@
    *   mediaSize: !print_preview.Setting,
    *   margins: !print_preview.Setting,
    *   dpi: !print_preview.Setting,
-   *   fitToPage: !print_preview.Setting,
    *   scaling: !print_preview.Setting,
+   *   scalingType: !print_preview.Setting,
+   *   scalingTypePdf: !print_preview.Setting,
    *   duplex: !print_preview.Setting,
    *   duplexShortEdge: !print_preview.Setting,
    *   cssBackground: !print_preview.Setting,
@@ -67,9 +68,10 @@
    *    isHeaderFooterEnabled: (boolean | undefined),
    *    isLandscapeEnabled: (boolean | undefined),
    *    isCollateEnabled: (boolean | undefined),
-   *    isFitToPageEnabled: (boolean | undefined),
    *    isCssBackgroundEnabled: (boolean | undefined),
    *    scaling: (string | undefined),
+   *    scalingType: (print_preview.ScalingType | undefined),
+   *    scalingTypePdf: (print_preview.ScalingType | undefined),
    *    vendor_options: (Object | undefined),
    *    isPinEnabled: (boolean | undefined),
    *    pinValue: (string | undefined)
@@ -147,8 +149,7 @@
 'use strict';
 
 /**
- * Sticky setting names. Alphabetical except for fitToPage, which must be set
- * after scaling in updateFromStickySettings().
+ * Sticky setting names in alphabetical order.
  * @type {!Array<string>}
  */
 const STICKY_SETTING_NAMES = [
@@ -164,9 +165,9 @@
   'layout',
   'margins',
   'mediaSize',
-  'customScaling',
   'scaling',
-  'fitToPage',
+  'scalingType',
+  'scalingTypePdf',
   'vendorItems',
 ];
 // <if expr="chromeos">
@@ -291,16 +292,6 @@
             key: 'dpi',
             updatesPreview: false,
           },
-          fitToPage: {
-            value: false,
-            unavailableValue: false,
-            valid: true,
-            available: true,
-            setByPolicy: false,
-            setFromUi: false,
-            key: 'isFitToPageEnabled',
-            updatesPreview: true,
-          },
           scaling: {
             value: '100',
             unavailableValue: '100',
@@ -311,14 +302,24 @@
             key: 'scaling',
             updatesPreview: true,
           },
-          customScaling: {
-            value: false,
-            unavailableValue: false,
+          scalingType: {
+            value: print_preview.ScalingType.DEFAULT,
+            unavailableValue: print_preview.ScalingType.DEFAULT,
             valid: true,
             available: true,
             setByPolicy: false,
             setFromUi: false,
-            key: 'customScaling',
+            key: 'scalingType',
+            updatesPreview: true,
+          },
+          scalingTypePdf: {
+            value: print_preview.ScalingType.DEFAULT,
+            unavailableValue: print_preview.ScalingType.DEFAULT,
+            valid: true,
+            available: true,
+            setByPolicy: false,
+            setFromUi: false,
+            key: 'scalingTypePdf',
             updatesPreview: true,
           },
           duplex: {
@@ -705,15 +706,15 @@
     const knownSizeToSaveAsPdf = isSaveAsPDF &&
         (!this.documentSettings.isModifiable ||
          this.documentSettings.hasCssMediaStyles);
-    this.setSettingPath_('fitToPage.unavailableValue', !isSaveAsPDF);
+    const scalingAvailable = !knownSizeToSaveAsPdf &&
+        (this.documentSettings.isModifiable || this.documentSettings.isPdf);
+    this.setSettingPath_('scaling.available', scalingAvailable);
     this.setSettingPath_(
-        'fitToPage.available',
-        !knownSizeToSaveAsPdf && this.documentSettings.isPdf);
+        'scalingType.available',
+        scalingAvailable && !this.documentSettings.isPdf);
     this.setSettingPath_(
-        'scaling.available',
-        !knownSizeToSaveAsPdf &&
-            (this.documentSettings.isModifiable ||
-             this.documentSettings.isPdf));
+        'scalingTypePdf.available',
+        scalingAvailable && this.documentSettings.isPdf);
     const caps = this.destination && this.destination.capabilities ?
         this.destination.capabilities.printer :
         null;
@@ -1021,21 +1022,14 @@
   },
 
   applyStickySettings: function() {
-    const defaultScaling = '100';
     if (this.stickySettings_) {
       STICKY_SETTING_NAMES.forEach(settingName => {
         const setting = this.get(settingName, this.settings);
         const value = this.stickySettings_[setting.key];
         if (value != undefined) {
           this.setSetting(settingName, value);
-        } else if (
-            settingName === 'customScaling' &&
-            !!this.stickySettings_['scaling']) {
-          // If users with an old set of sticky settings intentionally set a non
-          // default value, set customScaling to true so the value is restored.
-          // Otherwise, set to false with noSticky=true.
-          const scalingIsDefault = this.stickySettings_['scaling'] === '100';
-          this.setSetting(settingName, !scalingIsDefault, scalingIsDefault);
+        } else {
+          this.applyScalingStickySettings_(settingName);
         }
       });
     }
@@ -1056,6 +1050,39 @@
     this.fire('sticky-settings-changed', this.getStickySettings_());
   },
 
+  /**
+   * Helper function for applyStickySettings(). Checks if the setting
+   * is a scaling setting and applies by applying the old types
+   * that rely on 'fitToPage' and 'customScaling'.
+   * @param {string} settingName Name of the setting being applied.
+   * @private
+   */
+  applyScalingStickySettings_: function(settingName) {
+    // TODO(dhoss): Remove checks for 'customScaling' and 'fitToPage'
+    if (settingName === 'scalingType' &&
+        'customScaling' in this.stickySettings_) {
+      const isCustom = this.stickySettings_['customScaling'];
+      const scalingType = isCustom ? print_preview.ScalingType.CUSTOM :
+                                     print_preview.ScalingType.DEFAULT;
+      this.setSetting(settingName, scalingType);
+    } else if (settingName === 'scalingTypePdf') {
+      if ('isFitToPageEnabled' in this.stickySettings_) {
+        const isFitToPage = this.stickySettings_['isFitToPageEnabled'];
+        const scalingTypePdf = isFitToPage ?
+            print_preview.ScalingType.FIT_TO_PAGE :
+            this.getSetting('scalingType').value;
+        this.setSetting(settingName, scalingTypePdf);
+      } else if (
+          this.getSetting('scalingType').value ===
+          print_preview.ScalingType.CUSTOM) {
+        // In the event that 'isFitToPageEnabled' was not in the sticky
+        // settings, and 'scalingType' has been set to custom, we want
+        // 'scalingTypePdf' to match.
+        this.setSetting(settingName, print_preview.ScalingType.CUSTOM);
+      }
+    }
+  },
+
   // <if expr="chromeos">
   /**
    * Restricts settings and applies defaults as defined by policy applicable to
@@ -1207,6 +1234,9 @@
                     vendor_id: (number | undefined)}}
          */
         (this.getSettingValue('dpi'));
+    const scalingSettingKey = this.getSetting('scalingTypePdf').available ?
+        'scalingTypePdf' :
+        'scalingType';
     const ticket = {
       mediaSize: this.getSettingValue('mediaSize'),
       pageCount: this.getSettingValue('pages').length,
@@ -1221,15 +1251,12 @@
       shouldPrintBackgrounds: this.getSettingValue('cssBackground'),
       shouldPrintSelectionOnly: false,  // only used in print preview
       previewModifiable: this.documentSettings.isModifiable,
-      printToPDF: destination.id ==
-          print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
       printToGoogleDrive:
           destination.id == print_preview.Destination.GooglePromotedId.DOCS,
-      printWithCloudPrint: !destination.isLocal,
-      printWithPrivet: destination.isPrivet,
-      printWithExtension: destination.isExtension,
+      printerType: print_preview.getPrinterTypeForDestination(destination),
       rasterizePDF: this.getSettingValue('rasterize'),
-      scaleFactor: this.getSettingValue('customScaling') ?
+      scaleFactor: this.getSettingValue(scalingSettingKey) ===
+              print_preview.ScalingType.CUSTOM ?
           parseInt(this.getSettingValue('scaling'), 10) :
           100,
       pagesPerSheet: this.getSettingValue('pagesPerSheet'),
@@ -1237,7 +1264,9 @@
       dpiVertical: (dpi && 'vertical_dpi' in dpi) ? dpi.vertical_dpi : 0,
       dpiDefault: (dpi && 'is_default' in dpi) ? dpi.is_default : false,
       deviceName: destination.id,
-      fitToPageEnabled: this.getSettingValue('fitToPage'),
+      // TODO(dhoss): Pass the enum in the ticket.
+      fitToPageEnabled: this.getSettingValue(scalingSettingKey) ===
+          print_preview.ScalingType.FIT_TO_PAGE,
       pageWidth: this.pageSize.width,
       pageHeight: this.pageSize.height,
       showSystemDialog: showSystemDialog,
diff --git a/chrome/browser/resources/print_preview/data/scaling.html b/chrome/browser/resources/print_preview/data/scaling.html
new file mode 100644
index 0000000..cbaac09
--- /dev/null
+++ b/chrome/browser/resources/print_preview/data/scaling.html
@@ -0,0 +1,3 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+
+<script src="scaling.js"></script>
diff --git a/chrome/browser/resources/print_preview/data/scaling.js b/chrome/browser/resources/print_preview/data/scaling.js
new file mode 100644
index 0000000..be917a4
--- /dev/null
+++ b/chrome/browser/resources/print_preview/data/scaling.js
@@ -0,0 +1,19 @@
+// 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.
+
+cr.define('print_preview', function() {
+  'use strict';
+
+  /** @enum {number} */
+  const ScalingType = {
+    DEFAULT: 0,
+    FIT_TO_PAGE: 1,
+    CUSTOM: 2,
+  };
+
+  // Export
+  return {
+    ScalingType: ScalingType,
+  };
+});
diff --git a/chrome/browser/resources/print_preview/native_layer.js b/chrome/browser/resources/print_preview/native_layer.js
index 0aae1c1..8a5abc3 100644
--- a/chrome/browser/resources/print_preview/native_layer.js
+++ b/chrome/browser/resources/print_preview/native_layer.js
@@ -99,19 +99,6 @@
   let ProvisionalDestinationInfo;
 
   /**
-   * Printer types for capabilities and printer list requests.
-   * Should match PrinterType in print_preview_handler.h
-   * @enum {number}
-   */
-  const PrinterType = {
-    PRIVET_PRINTER: 0,
-    EXTENSION_PRINTER: 1,
-    PDF_PRINTER: 2,
-    LOCAL_PRINTER: 3,
-    CLOUD_PRINTER: 4
-  };
-
-  /**
    * An interface to the native Chromium printing system layer.
    */
   class NativeLayer {
@@ -330,7 +317,6 @@
     NativeLayer: NativeLayer,
     PreviewSettings: PreviewSettings,
     PrinterSetupResponse: PrinterSetupResponse,
-    PrinterType: PrinterType,
     PrivetPrinterDescription: PrivetPrinterDescription,
     ProvisionalDestinationInfo: ProvisionalDestinationInfo,
   };
diff --git a/chrome/browser/resources/print_preview/print_preview_resources.grd b/chrome/browser/resources/print_preview/print_preview_resources.grd
index d045f4a..2500566 100644
--- a/chrome/browser/resources/print_preview/print_preview_resources.grd
+++ b/chrome/browser/resources/print_preview/print_preview_resources.grd
@@ -145,6 +145,12 @@
       <structure name="IDR_PRINT_PREVIEW_DATA_DOCUMENT_INFO_JS"
                  file="data/document_info.js"
                  type="chrome_html" />
+      <structure name="IDR_PRINT_PREVIEW_DATA_SCALING_HTML"
+                 file="data/scaling.html"
+                 type="chrome_html" />
+      <structure name="IDR_PRINT_PREVIEW_DATA_SCALING_JS"
+                 file="data/scaling.js"
+                 type="chrome_html" />
       <structure name="IDR_PRINT_PREVIEW_DATA_SIZE_HTML"
                  file="data/size.html"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/print_preview/ui/BUILD.gn b/chrome/browser/resources/print_preview/ui/BUILD.gn
index 4c22b4c..5b144bb 100644
--- a/chrome/browser/resources/print_preview/ui/BUILD.gn
+++ b/chrome/browser/resources/print_preview/ui/BUILD.gn
@@ -232,6 +232,7 @@
   deps = [
     ":number_settings_section",
     ":settings_behavior",
+    "../data:scaling",
   ]
 }
 
@@ -317,6 +318,7 @@
     "../../pdf:pdf_scripting_api",
     "../data:coordinate2d",
     "../data:destination",
+    "../data:destination_match",
     "../data:margins",
     "../data:model",
     "../data:printable_area",
diff --git a/chrome/browser/resources/print_preview/ui/app.html b/chrome/browser/resources/print_preview/ui/app.html
index 2e5816e..d5ad5503 100644
--- a/chrome/browser/resources/print_preview/ui/app.html
+++ b/chrome/browser/resources/print_preview/ui/app.html
@@ -73,7 +73,8 @@
         cloud-print-error-message="[[cloudPrintErrorMessage_]]"
         destination-state="{{destinationState_}}"
         controls-managed="[[controlsManaged_]]" destination="{{destination_}}"
-        error="{{error_}}" new-print-preview-layout="[[newPrintPreviewLayout_]]"
+        error="{{error_}}" is-pdf="[[documentSettings_.isPdf]]"
+        new-print-preview-layout="[[newPrintPreviewLayout_]]"
         page-count="[[documentSettings_.pageCount]]"
         settings="[[settings]]" state="[[state]]" on-focus="onSidebarFocus_"
 <if expr="is_macosx">
diff --git a/chrome/browser/resources/print_preview/ui/preview_area.html b/chrome/browser/resources/print_preview/ui/preview_area.html
index ac0d828..2696ecfb 100644
--- a/chrome/browser/resources/print_preview/ui/preview_area.html
+++ b/chrome/browser/resources/print_preview/ui/preview_area.html
@@ -10,6 +10,7 @@
 <link rel="import" href="../dark_mode_behavior.html">
 <link rel="import" href="../data/coordinate2d.html">
 <link rel="import" href="../data/destination.html">
+<link rel="import" href="../data/destination_match.html">
 <link rel="import" href="../data/margins.html">
 <link rel="import" href="../data/model.html">
 <link rel="import" href="../data/printable_area.html">
diff --git a/chrome/browser/resources/print_preview/ui/preview_area.js b/chrome/browser/resources/print_preview/ui/preview_area.js
index 23329ca7..882c0f37 100644
--- a/chrome/browser/resources/print_preview/ui/preview_area.js
+++ b/chrome/browser/resources/print_preview/ui/preview_area.js
@@ -605,15 +605,16 @@
 
     // Simple settings: ranges, layout, header/footer, pages per sheet, fit to
     // page, css background, selection only, rasterize, scaling, dpi
-    if (!areRangesEqual(
-            /** @type {!Array<{from: number, to: number}>} */ (
-                this.getSettingValue('ranges')),
-            lastTicket.pageRange) ||
+    const isScalingTypeFitToPage = this.getSettingValue('scalingTypePdf') ===
+        print_preview.ScalingType.FIT_TO_PAGE;
+    if (isScalingTypeFitToPage !== lastTicket.fitToPageEnabled ||
+        !areRangesEqual(
+            /** @type {!Array<{from: number, to: number}>} */
+            (this.getSettingValue('ranges')), lastTicket.pageRange) ||
         this.getSettingValue('layout') !== lastTicket.landscape ||
         this.getColorForTicket_() !== lastTicket.color ||
         this.getSettingValue('headerFooter') !==
             lastTicket.headerFooterEnabled ||
-        this.getSettingValue('fitToPage') !== lastTicket.fitToPageEnabled ||
         this.getSettingValue('cssBackground') !==
             lastTicket.shouldPrintBackgrounds ||
         this.getSettingValue('selectionOnly') !==
@@ -643,12 +644,8 @@
     }
 
     // Destination
-    if (this.destination.isPrivet !== lastTicket.printWithPrivet ||
-        this.destination.isExtension !== lastTicket.printWithExtension ||
-        !this.destination.isLocal !== lastTicket.printWithCloudPrint ||
-        (lastTicket.printToPDF &&
-         this.destination.id !==
-             print_preview.Destination.GooglePromotedId.SAVE_AS_PDF)) {
+    if (print_preview.getPrinterTypeForDestination(this.destination) !==
+        lastTicket.printerType) {
       return true;
     }
 
@@ -663,7 +660,11 @@
 
   /** @return {number} Scale factor. */
   getScaleFactorForTicket_: function() {
-    return this.getSettingValue('customScaling') ?
+    const scalingSettingKey = this.getSetting('scalingTypePdf').available ?
+        'scalingTypePdf' :
+        'scalingType';
+    return this.getSettingValue(scalingSettingKey) ===
+            print_preview.ScalingType.CUSTOM ?
         parseInt(this.getSettingValue('scaling'), 10) :
         100;
   },
@@ -702,7 +703,8 @@
       isFirstRequest: this.inFlightRequestId_ == 0,
       requestID: this.inFlightRequestId_,
       previewModifiable: this.documentModifiable,
-      fitToPageEnabled: this.getSettingValue('fitToPage'),
+      fitToPageEnabled: this.getSettingValue('scalingTypePdf') ===
+          print_preview.ScalingType.FIT_TO_PAGE,
       scaleFactor: this.getScaleFactorForTicket_(),
       shouldPrintBackgrounds: this.getSettingValue('cssBackground'),
       shouldPrintSelectionOnly: this.getSettingValue('selectionOnly'),
@@ -717,11 +719,7 @@
       duplex: this.getSettingValue('duplex') ?
           print_preview.DuplexMode.LONG_EDGE :
           print_preview.DuplexMode.SIMPLEX,
-      printToPDF: this.destination.id ==
-          print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
-      printWithCloudPrint: !this.destination.isLocal,
-      printWithPrivet: this.destination.isPrivet,
-      printWithExtension: this.destination.isExtension,
+      printerType: print_preview.getPrinterTypeForDestination(this.destination),
       rasterizePDF: this.getSettingValue('rasterize'),
     };
 
diff --git a/chrome/browser/resources/print_preview/ui/scaling_settings.html b/chrome/browser/resources/print_preview/ui/scaling_settings.html
index d4cd7a3..5c3e8fc 100644
--- a/chrome/browser/resources/print_preview/ui/scaling_settings.html
+++ b/chrome/browser/resources/print_preview/ui/scaling_settings.html
@@ -2,6 +2,7 @@
 
 <link rel="import" href="chrome://resources/cr_elements/md_select_css.html">
 <link rel="import" href="chrome://resources/polymer/v1_0/iron-collapse/iron-collapse.html">
+<link rel="import" href="../data/scaling.html">
 <link rel="import" href="number_settings_section.html">
 <link rel="import" href="print_preview_shared_css.html">
 <link rel="import" href="select_behavior.html">
@@ -20,8 +21,7 @@
           <option value="[[ScalingValue.DEFAULT]]">
             $i18n{optionDefaultScaling}
           </option>
-          <option value="[[ScalingValue.FIT_TO_PAGE]]"
-              hidden$="[[!settings.fitToPage.available]]">
+          <option value="[[ScalingValue.FIT_TO_PAGE]]" hidden$="[[!isPdf]]">
             $i18n{optionFitToPage}
           </option>
           <option value="[[ScalingValue.CUSTOM]]">
diff --git a/chrome/browser/resources/print_preview/ui/scaling_settings.js b/chrome/browser/resources/print_preview/ui/scaling_settings.js
index 909562b..3c53781 100644
--- a/chrome/browser/resources/print_preview/ui/scaling_settings.js
+++ b/chrome/browser/resources/print_preview/ui/scaling_settings.js
@@ -4,13 +4,6 @@
 
 cr.exportPath('print_preview');
 
-/** @enum {number} */
-const ScalingValue = {
-  DEFAULT: 0,
-  FIT_TO_PAGE: 1,
-  CUSTOM: 2,
-};
-
 /*
  * When fit to page is available, the checkbox and input interact as follows:
  * 1. When checkbox is checked, the fit to page scaling value is displayed in
@@ -30,6 +23,8 @@
       observer: 'onDisabledChanged_',
     },
 
+    isPdf: Boolean,
+
     /** @private {string} */
     currentValue_: {
       type: String,
@@ -39,8 +34,8 @@
     /** @private {boolean} */
     customSelected_: {
       type: Boolean,
-      computed: 'computeCustomSelected_(settings.customScaling.*, ' +
-          'settings.fitToPage.*)',
+      computed: 'computeCustomSelected_(settingKey_, ' +
+          'settings.scalingType.*, settings.scalingTypePdf.*)',
     },
 
     /** @private {boolean} */
@@ -52,14 +47,23 @@
       value: false,
     },
 
+    /** @private {string} */
+    settingKey_: {
+      type: String,
+      computed: 'computeSettingKey_(isPdf)',
+    },
+
     /** Mirroring the enum so that it can be used from HTML bindings. */
-    ScalingValue: Object,
+    ScalingValue: {
+      type: Object,
+      value: print_preview.ScalingType,
+    },
   },
 
   observers: [
-    'onFitToPageSettingChange_(settings.fitToPage.value)',
+    'onScalingTypeSettingChanged_(settingKey_, settings.scalingType.value, ' +
+        'settings.scalingTypePdf.value)',
     'onScalingSettingChanged_(settings.scaling.value)',
-    'onCustomScalingSettingChanged_(settings.customScaling.value)',
   ],
 
   /** @private {string} */
@@ -81,28 +85,24 @@
    */
   userSelectedCustomScaling_: false,
 
-  /** @override */
-  ready: function() {
-    this.ScalingValue = ScalingValue;
-  },
-
   onProcessSelectChange: function(value) {
-    if (value === ScalingValue.FIT_TO_PAGE.toString()) {
-      this.setSetting('fitToPage', true);
-      return;
-    }
-
-    const fitToPageAvailable = this.getSetting('fitToPage').available;
-    if (fitToPageAvailable) {
-      this.setSetting('fitToPage', false);
-    }
-    const isCustom = value === ScalingValue.CUSTOM.toString();
+    const isCustom = value === print_preview.ScalingType.CUSTOM.toString();
     if (isCustom && !this.customScalingSettingSet_) {
       this.userSelectedCustomScaling_ = true;
     } else {
       this.customScalingSettingSet_ = false;
     }
-    this.setSetting('customScaling', isCustom);
+
+    const valueAsNumber = parseInt(value, 10);
+    if (value !== print_preview.ScalingType.FIT_TO_PAGE.toString()) {
+      this.setSetting('scalingType', valueAsNumber);
+    }
+    if (this.isPdf ||
+        this.getSetting('scalingTypePdf').value !==
+            print_preview.ScalingType.FIT_TO_PAGE) {
+      this.setSetting('scalingTypePdf', valueAsNumber);
+    }
+
     if (isCustom) {
       this.setSetting('scaling', this.currentValue_);
     }
@@ -117,35 +117,6 @@
     }
   },
 
-  /** @private */
-  onFitToPageSettingChange_: function() {
-    if (!this.getSettingValue('fitToPage') ||
-        !this.getSetting('fitToPage').available) {
-      return;
-    }
-
-    this.updateScalingToValid_();
-    this.selectedValue = ScalingValue.FIT_TO_PAGE.toString();
-  },
-
-  /** @private */
-  onCustomScalingSettingChanged_: function() {
-    if (this.getSettingValue('fitToPage') &&
-        this.getSetting('fitToPage').available) {
-      return;
-    }
-
-    const isCustom =
-        /** @type {boolean} */ (this.getSetting('customScaling').value);
-    if (!isCustom) {
-      this.updateScalingToValid_();
-    } else {
-      this.customScalingSettingSet_ = true;
-    }
-    this.selectedValue = isCustom ? ScalingValue.CUSTOM.toString() :
-                                    ScalingValue.DEFAULT.toString();
-  },
-
   /**
    * Updates the input string when scaling setting is set.
    * @private
@@ -156,6 +127,22 @@
     this.currentValue_ = value;
   },
 
+  /** @private */
+  onScalingTypeSettingChanged_: function() {
+    if (!this.settingKey_) {
+      return;
+    }
+
+    const value = /** @type {!print_preview.ScalingType} */
+        (this.getSettingValue(this.settingKey_));
+    if (value !== print_preview.ScalingType.CUSTOM) {
+      this.updateScalingToValid_();
+    } else {
+      this.customScalingSettingSet_ = true;
+    }
+    this.selectedValue = value.toString();
+  },
+
   /**
    * Updates scaling and fit to page settings based on the validity and current
    * value of the scaling input.
@@ -188,9 +175,17 @@
    * @private
    */
   computeCustomSelected_: function() {
-    return /** @type {boolean} */ (this.getSettingValue('customScaling')) &&
-        (!this.getSetting('fitToPage').available ||
-         !(/** @type {boolean} */ (this.getSettingValue('fitToPage'))));
+    return !!this.settingKey_ &&
+        this.getSettingValue(this.settingKey_) ===
+        print_preview.ScalingType.CUSTOM;
+  },
+
+  /**
+   * @return {string} The key of the appropriate scaling setting.
+   * @private
+   */
+  computeSettingKey_: function() {
+    return this.isPdf ? 'scalingTypePdf' : 'scalingType';
   },
 
   /** @private */
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.html b/chrome/browser/resources/print_preview/ui/sidebar.html
index b2270abe..7fffd00 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.html
+++ b/chrome/browser/resources/print_preview/ui/sidebar.html
@@ -168,7 +168,7 @@
             hidden$="[[!settings.dpi.available]]" class="settings-section">
         </print-preview-dpi-settings>
         <print-preview-scaling-settings settings="[[settings]]"
-            disabled="[[controlsDisabled_]]"
+            disabled="[[controlsDisabled_]]" is-pdf="[[isPdf]]"
             hidden$="[[!settings.scaling.available]]"
             class="settings-section">
         </print-preview-scaling-settings>
diff --git a/chrome/browser/resources/print_preview/ui/sidebar.js b/chrome/browser/resources/print_preview/ui/sidebar.js
index 687539f8..fe77e9d7 100644
--- a/chrome/browser/resources/print_preview/ui/sidebar.js
+++ b/chrome/browser/resources/print_preview/ui/sidebar.js
@@ -47,6 +47,8 @@
       notify: true,
     },
 
+    isPdf: Boolean,
+
     newPrintPreviewLayout: {
       type: Boolean,
       reflectToAttribute: true,
diff --git a/chrome/browser/resources/settings/internet_page/internet_detail_page.js b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
index bfdffb2..7c15915 100644
--- a/chrome/browser/resources/settings/internet_page/internet_detail_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_detail_page.js
@@ -551,7 +551,7 @@
    * @private
    */
   getDefaultConfigProperties_: function() {
-    return {type: this.managedProperties_.type};
+    return OncMojo.getDefaultConfigProperties(this.managedProperties_.type);
   },
 
   /**
@@ -1169,14 +1169,14 @@
     OncMojo.setConfigProperty(config, field, value);
     // Ensure that any required configuration properties for partial
     // configurations are set.
-    if (config.vpn) {
-      config.vpn.type = this.managedProperties_.typeProperties.vpn.type;
-      if (config.vpn.openVpn &&
-          config.vpn.openVpn.saveCredentials == undefined) {
-        config.vpn.openVpn.saveCredentials = false;
+    const vpnConfig = config.typeConfig.vpn;
+    if (vpnConfig) {
+      vpnConfig.type = this.managedProperties_.typeProperties.vpn.type;
+      if (vpnConfig.openVpn && vpnConfig.openVpn.saveCredentials == undefined) {
+        vpnConfig.openVpn.saveCredentials = false;
       }
-      if (config.vpn.l2tp && config.vpn.l2tp.saveCredentials == undefined) {
-        config.vpn.l2tp.saveCredentials = false;
+      if (vpnConfig.l2tp && vpnConfig.l2tp.saveCredentials == undefined) {
+        vpnConfig.l2tp.saveCredentials = false;
       }
     }
     this.setMojoNetworkProperties_(config);
@@ -1192,7 +1192,7 @@
     }
     const config = this.getDefaultConfigProperties_();
     const apn = event.detail;
-    config.cellular = {apn: apn};
+    config.typeConfig.cellular = {apn: apn};
     this.setMojoNetworkProperties_(config);
   },
 
diff --git a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
index cb5de26..3351442 100644
--- a/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
+++ b/chrome/browser/resources/settings/internet_page/internet_known_networks_page.js
@@ -191,14 +191,18 @@
   /** @private */
   onRemovePreferredTap_: function() {
     assert(this.networkType !== undefined);
-    this.setProperties_({type: this.networkType, priority: {value: 0}});
+    const config = OncMojo.getDefaultConfigProperties(this.networkType);
+    config.priority = {value: 0};
+    this.setProperties_(config);
     /** @type {!CrActionMenuElement} */ (this.$.dotsMenu).close();
   },
 
   /** @private */
   onAddPreferredTap_: function() {
     assert(this.networkType !== undefined);
-    this.setProperties_({type: this.networkType, priority: {value: 1}});
+    const config = OncMojo.getDefaultConfigProperties(this.networkType);
+    config.priority = {value: 1};
+    this.setProperties_(config);
     /** @type {!CrActionMenuElement} */ (this.$.dotsMenu).close();
   },
 
diff --git a/chrome/browser/resources/tab_strip/BUILD.gn b/chrome/browser/resources/tab_strip/BUILD.gn
index f674d715..faba495 100644
--- a/chrome/browser/resources/tab_strip/BUILD.gn
+++ b/chrome/browser/resources/tab_strip/BUILD.gn
@@ -33,14 +33,9 @@
   deps = [
     "//ui/webui/resources/js:icon.m",
   ]
-  externs_list = [ "$externs_path/chrome.js" ]
 }
 
 js_library("tab_list") {
-  deps = [
-    ":types",
-  ]
-  externs_list = [ "$externs_path/chrome.js" ]
 }
 
 js_library("tab_strip_view_proxy") {
@@ -49,9 +44,6 @@
   ]
 }
 
-js_library("types") {
-}
-
 group("tab_strip_modules") {
   deps = [
     ":alert_indicator_module",
diff --git a/chrome/browser/resources/tab_strip/tab.html b/chrome/browser/resources/tab_strip/tab.html
index dac4fd9..ab46a7a 100644
--- a/chrome/browser/resources/tab_strip/tab.html
+++ b/chrome/browser/resources/tab_strip/tab.html
@@ -47,7 +47,7 @@
     width: var(--favicon-size);
   }
 
-  #loading,
+  #progressSpinner,
   #favicon,
   #crashedIcon {
     height: var(--favicon-size);
@@ -58,11 +58,10 @@
     width: var(--favicon-size);
   }
 
-  #loading {
+  #progressSpinner {
     -webkit-mask:
         url(chrome://resources/images/throbber_small.svg)
         center/contain no-repeat;
-    background-color: var(--tabstrip-tab-loading-spinning-color);
     display: none;
   }
 
@@ -93,8 +92,8 @@
     width: 6px;
   }
 
-  :host([loading]) #loading,
-  :host([loading]) #favicon {
+  :host([waiting]) #progressSpinner,
+  :host([loading]) #progressSpinner {
     display: block;
   }
 
@@ -105,6 +104,20 @@
     width: calc(var(--favicon-size) - 6px);
   }
 
+  :host([waiting]) #progressSpinner {
+    background-color: var(--tabstrip-tab-waiting-spinning-color);
+    transform: /* Center first, then flip horizontally. */
+              translate(-50%, -50%) scaleX(-1);
+  }
+
+  :host([waiting]) #favicon {
+    display: none;
+  }
+
+  :host([loading]) #progressSpinner {
+    background-color: var(--tabstrip-tab-loading-spinning-color);
+  }
+
   :host([crashed]) #favicon {
     opacity: 0;
     transform: translate(-50%, 100%);
@@ -222,7 +235,7 @@
 <div id="dragImage">
   <header id="title">
     <div id="faviconContainer">
-      <div id="loading"></div>
+      <div id="progressSpinner"></div>
       <div id="favicon"></div>
       <div id="crashedIcon"></div>
       <div id="blocked"></div>
diff --git a/chrome/browser/resources/tab_strip/tab.js b/chrome/browser/resources/tab_strip/tab.js
index 052bc59..82dcd8884 100644
--- a/chrome/browser/resources/tab_strip/tab.js
+++ b/chrome/browser/resources/tab_strip/tab.js
@@ -5,16 +5,10 @@
 import {getFavicon, getFaviconForPageURL} from 'chrome://resources/js/icon.m.js';
 
 import {CustomElement} from './custom_element.js';
-import {TabsApiProxy} from './tabs_api_proxy.js';
+import {TabData, TabNetworkState, TabsApiProxy} from './tabs_api_proxy.js';
 
 export const DEFAULT_ANIMATION_DURATION = 125;
 
-/** @const @enum {string} */
-const STATUS = {
-  LOADING: 'loading',
-  COMPLETE: 'complete',
-};
-
 export class TabElement extends CustomElement {
   static get template() {
     return `{__html_template__}`;
@@ -45,7 +39,7 @@
     this.thumbnail_ =
         /** @type {!Image} */ (this.shadowRoot.querySelector('#thumbnailImg'));
 
-    /** @private {!Tab} */
+    /** @private {!TabData} */
     this.tab_;
 
     /** @private {!TabsApiProxy} */
@@ -56,20 +50,26 @@
         this.shadowRoot.querySelector('#titleText'));
 
     this.addEventListener('click', this.onClick_.bind(this));
+    this.addEventListener('contextmenu', this.onContextMenu_.bind(this));
     this.closeButtonEl_.addEventListener('click', this.onClose_.bind(this));
   }
 
-  /** @return {!Tab} */
+  /** @return {!TabData} */
   get tab() {
     return this.tab_;
   }
 
-  /** @param {!Tab} tab */
+  /** @param {!TabData} tab */
   set tab(tab) {
     this.toggleAttribute('active', tab.active);
-    // TODO(johntlee): Update loading status to also be based on whether
-    // the tab is navigating to a new document.
-    this.toggleAttribute('loading', tab.status === STATUS.LOADING);
+    this.toggleAttribute(
+        'waiting',
+        !tab.shouldHideThrobber &&
+            tab.networkState === TabNetworkState.WAITING);
+    this.toggleAttribute(
+        'loading',
+        !tab.shouldHideThrobber &&
+            tab.networkState === TabNetworkState.LOADING);
     this.toggleAttribute('pinned', tab.pinned);
     this.setAttribute('draggable', tab.pinned);
 
@@ -82,7 +82,8 @@
         this.faviconEl_.style.backgroundImage = getFavicon(tab.favIconUrl);
       }
     } else {
-      if (tab.status === STATUS.COMPLETE) {
+      if (tab.networkState === TabNetworkState.NONE ||
+          tab.networkState === TabNetworkState.ERROR) {
         // If the tab has finished loading and there still is no favicon,
         // fallback to a favicon generated by the page URL. This guarantees
         // there is always some favicon, even if it's the default icon.
@@ -126,6 +127,18 @@
     this.tabsApi_.activateTab(this.tab_.id);
   }
 
+  /** @private */
+  onContextMenu_(event) {
+    event.preventDefault();
+
+    if (!this.tab_) {
+      return;
+    }
+
+    this.tabsApi_.showTabContextMenu(
+        this.tab_.id, event.clientX, event.clientY);
+  }
+
   /**
    * @param {!Event} event
    * @private
diff --git a/chrome/browser/resources/tab_strip/tab_list.js b/chrome/browser/resources/tab_strip/tab_list.js
index a0ccee46..d7736c8 100644
--- a/chrome/browser/resources/tab_strip/tab_list.js
+++ b/chrome/browser/resources/tab_strip/tab_list.js
@@ -10,7 +10,7 @@
 import {CustomElement} from './custom_element.js';
 import {TabElement} from './tab.js';
 import {TabStripViewProxy} from './tab_strip_view_proxy.js';
-import {TabsApiProxy} from './tabs_api_proxy.js';
+import {TabData, TabsApiProxy} from './tabs_api_proxy.js';
 
 /**
  * The amount of padding to leave between the edge of the screen and the active
@@ -111,7 +111,7 @@
   }
 
   /**
-   * @param {!Tab} tab
+   * @param {!TabData} tab
    * @return {!TabElement}
    * @private
    */
@@ -248,20 +248,20 @@
   onTabActivated_(tabId) {
     const previouslyActiveTab = this.getActiveTab_();
     if (previouslyActiveTab) {
-      previouslyActiveTab.tab = /** @type {!Tab} */ (
+      previouslyActiveTab.tab = /** @type {!TabData} */ (
           Object.assign({}, previouslyActiveTab.tab, {active: false}));
     }
 
     const newlyActiveTab = this.findTabElement_(tabId);
     if (newlyActiveTab) {
-      newlyActiveTab.tab = /** @type {!Tab} */ (
+      newlyActiveTab.tab = /** @type {!TabData} */ (
           Object.assign({}, newlyActiveTab.tab, {active: true}));
       this.moveOrScrollToActiveTab_();
     }
   }
 
   /**
-   * @param {!Tab} tab
+   * @param {!TabData} tab
    * @private
    */
   onTabCreated_(tab) {
@@ -311,7 +311,7 @@
   }
 
   /**
-   * @param {!Tab} tab
+   * @param {!TabData} tab
    * @private
    */
   onTabUpdated_(tab) {
diff --git a/chrome/browser/resources/tab_strip/tabs_api_proxy.js b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
index e685568..1182936c 100644
--- a/chrome/browser/resources/tab_strip/tabs_api_proxy.js
+++ b/chrome/browser/resources/tab_strip/tabs_api_proxy.js
@@ -4,10 +4,40 @@
 
 import {addSingletonGetter, sendWithPromise} from 'chrome://resources/js/cr.m.js';
 
+/**
+ * Must be kept in sync with TabNetworkState from
+ * //chrome/browser/ui/tabs/tab_network_state.h.
+ * @enum {number}
+ */
+export const TabNetworkState = {
+  NONE: 0,
+  WAITING: 1,
+  LOADING: 2,
+  ERROR: 3,
+};
+
+/**
+ * @typedef {{
+ *    active: boolean,
+ *    favIconUrl: string,
+ *    id: number,
+ *    index: number,
+ *    networkState: !TabNetworkState,
+ *    pinned: boolean,
+ *    shouldHideThrobber: boolean,
+ *    title: string,
+ *    url: string,
+ * }}
+ */
+export let TabData;
+
+/** @typedef {!Tab} */
+let ExtensionsApiTab;
+
 export class TabsApiProxy {
   /**
    * @param {number} tabId
-   * @return {!Promise<!Tab>}
+   * @return {!Promise<!ExtensionsApiTab>}
    */
   activateTab(tabId) {
     return new Promise(resolve => {
@@ -16,7 +46,7 @@
   }
 
   /**
-   * @return {!Promise<!Array<!Tab>>}
+   * @return {!Promise<!Array<!TabData>>}
    */
   getTabs() {
     return sendWithPromise('getTabs');
@@ -35,7 +65,7 @@
   /**
    * @param {number} tabId
    * @param {number} newIndex
-   * @return {!Promise<!Tab>}
+   * @return {!Promise<!ExtensionsApiTab>}
    */
   moveTab(tabId, newIndex) {
     return new Promise(resolve => {
@@ -51,6 +81,15 @@
   trackThumbnailForTab(tabId) {
     chrome.send('addTrackedTab', [tabId]);
   }
+
+  /**
+   * @param {number} tabId
+   * @param {number} locationX
+   * @param {number} locationY
+   */
+  showTabContextMenu(tabId, locationX, locationY) {
+    chrome.send('showTabContextMenu', [tabId, locationX, locationY]);
+  }
 }
 
 addSingletonGetter(TabsApiProxy);
diff --git a/chrome/browser/resources/tab_strip/types.js b/chrome/browser/resources/tab_strip/types.js
deleted file mode 100644
index d282c59f..0000000
--- a/chrome/browser/resources/tab_strip/types.js
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-/**
- * @fileoverview Closure typedefs for Tab Strip.
- */
-
-/**
- * @typedef {{
- *    tabId: number,
- *    windowId: number,
- * }}
- */
-let TabActivatedInfo;
-
-/**
- * @typedef {{
- *    newPosition: number,
- *    newWindowId: number,
- * }}
- */
-let TabAttachedInfo;
-
-/**
- * @typedef {{
- *    oldPosition: number,
- *    oldWindowId: number,
- * }}
- */
-let TabDetachedInfo;
-
-/**
- * @typedef {{
- *    fromIndex: number,
- *    toIndex: number,
- *    windowId: number,
- * }}
- */
-let TabMovedInfo;
-
-/**
- * @typedef {{
- *    isWindowClosing: boolean,
- *    windowId: number,
- * }}
- */
-let WindowRemoveInfo;
diff --git a/chrome/browser/secure_origin_whitelist_browsertest.cc b/chrome/browser/secure_origin_whitelist_browsertest.cc
index fc04f66..37afd4f 100644
--- a/chrome/browser/secure_origin_whitelist_browsertest.cc
+++ b/chrome/browser/secure_origin_whitelist_browsertest.cc
@@ -168,17 +168,26 @@
   }
 }
 
+class SecureOriginWhitelistBrowsertestWithMarkHttpDangerous
+    : public SecureOriginWhitelistBrowsertest {
+ public:
+  SecureOriginWhitelistBrowsertestWithMarkHttpDangerous() {
+    // TODO(crbug.com/917693): Remove this forced feature/param when the feature
+    // fully launches.
+    feature_list_.InitAndEnableFeatureWithParameters(
+        security_state::features::kMarkHttpAsFeature,
+        {{security_state::features::kMarkHttpAsFeatureParameterName,
+          security_state::features::kMarkHttpAsParameterDangerous}});
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Tests that whitelisted insecure origins are correctly set as security level
 // NONE instead of the default level DANGEROUS.
-IN_PROC_BROWSER_TEST_P(SecureOriginWhitelistBrowsertest, SecurityIndicators) {
-  // TODO(crbug.com/917693): Remove this forced feature/param when the feature
-  // fully launches.
-  base::test::ScopedFeatureList scoped_feature_list;
-  scoped_feature_list.InitAndEnableFeatureWithParameters(
-      security_state::features::kMarkHttpAsFeature,
-      {{security_state::features::kMarkHttpAsFeatureParameterName,
-        security_state::features::kMarkHttpAsParameterDangerous}});
-
+IN_PROC_BROWSER_TEST_P(SecureOriginWhitelistBrowsertestWithMarkHttpDangerous,
+                       SecurityIndicators) {
   ui_test_utils::NavigateToURL(
       browser(),
       embedded_test_server()->GetURL(
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
index fe77b098..917b523 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_context_menu_observer_unittest.cc
@@ -49,7 +49,7 @@
 
 class MockSharingDeviceRegistration : public SharingDeviceRegistration {
  public:
-  explicit MockSharingDeviceRegistration()
+  MockSharingDeviceRegistration()
       : SharingDeviceRegistration(/* pref_service_= */ nullptr,
                                   /* sharing_sync_preference_= */ nullptr,
                                   /* instance_id_driver_= */ nullptr,
@@ -130,6 +130,7 @@
           base::StrCat({"guid", base::NumberToString(i)}), "name",
           "chrome_version", "user_agent",
           sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
+          base::SysInfo::HardwareInfo(),
           /*last_updated_timestamp=*/base::Time::Now(),
           /*send_tab_to_self_receiving_enabled=*/false,
           /*sharing_info=*/base::nullopt));
diff --git a/chrome/browser/sharing/click_to_call/click_to_call_ui_controller_unittest.cc b/chrome/browser/sharing/click_to_call/click_to_call_ui_controller_unittest.cc
index 15758415..c6d0534 100644
--- a/chrome/browser/sharing/click_to_call/click_to_call_ui_controller_unittest.cc
+++ b/chrome/browser/sharing/click_to_call/click_to_call_ui_controller_unittest.cc
@@ -121,12 +121,13 @@
 
 // Check the call to sharing service when a device is chosen.
 TEST_F(ClickToCallUiControllerTest, OnDeviceChosen) {
-  syncer::DeviceInfo device_info(
-      kReceiverGuid, kReceiverName, "chrome_version", "user_agent",
-      sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
-      /*last_updated_timestamp=*/base::Time::Now(),
-      /*send_tab_to_self_receiving_enabled=*/false,
-      /*sharing_info=*/base::nullopt);
+  syncer::DeviceInfo device_info(kReceiverGuid, kReceiverName, "chrome_version",
+                                 "user_agent",
+                                 sync_pb::SyncEnums_DeviceType_TYPE_PHONE,
+                                 "device_id", base::SysInfo::HardwareInfo(),
+                                 /*last_updated_timestamp=*/base::Time::Now(),
+                                 /*send_tab_to_self_receiving_enabled=*/false,
+                                 /*sharing_info=*/base::nullopt);
 
   chrome_browser_sharing::SharingMessage sharing_message;
   sharing_message.mutable_click_to_call_message()->set_phone_number(
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
index 5d9cd4ae..c19fba5 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_context_menu_observer_unittest.cc
@@ -45,7 +45,7 @@
 
 class MockSharingDeviceRegistration : public SharingDeviceRegistration {
  public:
-  explicit MockSharingDeviceRegistration()
+  MockSharingDeviceRegistration()
       : SharingDeviceRegistration(/* pref_service_= */ nullptr,
                                   /* sharing_sync_preference_= */ nullptr,
                                   /* instance_id_driver_= */ nullptr,
@@ -127,6 +127,7 @@
           base::StrCat({"guid", base::NumberToString(i)}), "name",
           "chrome_version", "user_agent",
           sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
+          base::SysInfo::HardwareInfo(),
           /*last_updated_timestamp=*/base::Time::Now(),
           /*send_tab_to_self_receiving_enabled=*/false,
           /*sharing_info=*/base::nullopt));
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_message_handler_desktop_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_message_handler_desktop_unittest.cc
index 99afbac..8ee2c516 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_message_handler_desktop_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_message_handler_desktop_unittest.cc
@@ -34,7 +34,7 @@
 
 class MockSharingDeviceRegistration : public SharingDeviceRegistration {
  public:
-  explicit MockSharingDeviceRegistration()
+  MockSharingDeviceRegistration()
       : SharingDeviceRegistration(/* pref_service_= */ nullptr,
                                   /* sharing_sync_preference_= */ nullptr,
                                   /* instance_id_driver_= */ nullptr,
@@ -160,6 +160,7 @@
                   /*chrome_version=*/"78.0.0.0",
                   /*sync_user_agent=*/"Chrome", sync_pb::SyncEnums::TYPE_LINUX,
                   /*signin_scoped_device_id=*/base::GenerateGUID(),
+                  base::SysInfo::HardwareInfo(),
                   /*last_updated_timestamp=*/base::Time::Now(),
                   /*send_tab_to_self_receiving_enabled=*/false,
                   /*sharing_info=*/base::nullopt);
diff --git a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc b/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
index f7cc196..862f8fb 100644
--- a/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
+++ b/chrome/browser/sharing/shared_clipboard/shared_clipboard_ui_controller_unittest.cc
@@ -93,12 +93,13 @@
           return std::make_unique<testing::NiceMock<MockSharingService>>(
               std::make_unique<SharingFCMHandler>(nullptr, nullptr, nullptr));
         }));
-    syncer::DeviceInfo device_info(
-        kReceiverGuid, kReceiverName, "chrome_version", "user_agent",
-        sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
-        /*last_updated_timestamp=*/base::Time::Now(),
-        /*send_tab_to_self_receiving_enabled=*/false,
-        /*sharing_info=*/base::nullopt);
+    syncer::DeviceInfo device_info(kReceiverGuid, kReceiverName,
+                                   "chrome_version", "user_agent",
+                                   sync_pb::SyncEnums_DeviceType_TYPE_PHONE,
+                                   "device_id", base::SysInfo::HardwareInfo(),
+                                   /*last_updated_timestamp=*/base::Time::Now(),
+                                   /*send_tab_to_self_receiving_enabled=*/false,
+                                   /*sharing_info=*/base::nullopt);
     controller_ = SharedClipboardUiController::GetOrCreateFromWebContents(
         web_contents_.get());
     controller_->OnDeviceSelected(base::UTF8ToUTF16(kText), device_info);
@@ -126,12 +127,13 @@
 
 // Check the call to sharing service when a device is chosen.
 TEST_F(SharedClipboardUiControllerTest, OnDeviceChosen) {
-  syncer::DeviceInfo device_info(
-      kReceiverGuid, kReceiverName, "chrome_version", "user_agent",
-      sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
-      /*last_updated_timestamp=*/base::Time::Now(),
-      /*send_tab_to_self_receiving_enabled=*/false,
-      /*sharing_info=*/base::nullopt);
+  syncer::DeviceInfo device_info(kReceiverGuid, kReceiverName, "chrome_version",
+                                 "user_agent",
+                                 sync_pb::SyncEnums_DeviceType_TYPE_PHONE,
+                                 "device_id", base::SysInfo::HardwareInfo(),
+                                 /*last_updated_timestamp=*/base::Time::Now(),
+                                 /*send_tab_to_self_receiving_enabled=*/false,
+                                 /*sharing_info=*/base::nullopt);
 
   chrome_browser_sharing::SharingMessage sharing_message;
   sharing_message.mutable_shared_clipboard_message()->set_text(kExpectedText);
diff --git a/chrome/browser/sharing/sharing_browsertest.cc b/chrome/browser/sharing/sharing_browsertest.cc
index 0f63cb2..483005b5 100644
--- a/chrome/browser/sharing/sharing_browsertest.cc
+++ b/chrome/browser/sharing/sharing_browsertest.cc
@@ -87,7 +87,7 @@
                 {"testing_device_", base::NumberToString(device_id++)}),
             device->chrome_version(), device->sync_user_agent(),
             device->device_type(), device->signin_scoped_device_id(),
-            device->last_updated_timestamp(),
+            device->hardware_info(), device->last_updated_timestamp(),
             device->send_tab_to_self_receiving_enabled(),
             device->sharing_info());
     fake_device_info_tracker_.Add(fake_device.get());
diff --git a/chrome/browser/sharing/sharing_fcm_handler_unittest.cc b/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
index 7d2bdfc..3a32ac75 100644
--- a/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
+++ b/chrome/browser/sharing/sharing_fcm_handler_unittest.cc
@@ -64,6 +64,7 @@
     fake_device_info_ = std::make_unique<syncer::DeviceInfo>(
         kSenderGuid, kSenderName, "chrome_version", "user_agent",
         sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+        base::SysInfo::HardwareInfo(),
         /*last_updated_timestamp=*/base::Time::Now(),
         /*send_tab_to_self_receiving_enabled=*/false,
         syncer::DeviceInfo::SharingInfo(
diff --git a/chrome/browser/sharing/sharing_service_unittest.cc b/chrome/browser/sharing/sharing_service_unittest.cc
index 4d25270..63e37500 100644
--- a/chrome/browser/sharing/sharing_service_unittest.cc
+++ b/chrome/browser/sharing/sharing_service_unittest.cc
@@ -207,6 +207,7 @@
     return std::make_unique<syncer::DeviceInfo>(
         id, name, "chrome_version", "user_agent",
         sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+        base::SysInfo::HardwareInfo(),
         /*last_updated_timestamp=*/base::Time::Now(),
         /*send_tab_to_self_receiving_enabled=*/false,
         syncer::DeviceInfo::SharingInfo(
diff --git a/chrome/browser/sharing/sharing_sync_preference_unittest.cc b/chrome/browser/sharing/sharing_sync_preference_unittest.cc
index bc7b692..38b040e821 100644
--- a/chrome/browser/sharing/sharing_sync_preference_unittest.cc
+++ b/chrome/browser/sharing/sharing_sync_preference_unittest.cc
@@ -152,6 +152,7 @@
       std::make_unique<syncer::DeviceInfo>(
           kDeviceGuid, kDeviceName, "chrome_version", "user_agent",
           sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "device_id",
+          base::SysInfo::HardwareInfo(),
           /*last_updated_timestamp=*/base::Time::Now(),
           /*send_tab_to_self_receiving_enabled=*/false,
           /*sharing_info=*/base::nullopt);
diff --git a/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc b/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
index ba609de..dc4a3fd 100644
--- a/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
+++ b/chrome/browser/site_isolation/chrome_site_per_process_browsertest.cc
@@ -1781,14 +1781,23 @@
   EXPECT_EQ(first_web_contents, tab_strip_model->GetActiveWebContents());
 }
 
+class ChromeSitePerProcessTestWithVerifiedUserActivation
+    : public ChromeSitePerProcessTest {
+ public:
+  ChromeSitePerProcessTestWithVerifiedUserActivation() {
+    feature_list_.InitAndEnableFeature(
+        features::kBrowserVerifiedUserActivation);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Test mouse down activation notification with browser verification.
-IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTest,
+IN_PROC_BROWSER_TEST_F(ChromeSitePerProcessTestWithVerifiedUserActivation,
                        UserActivationBrowserVerificationSameOriginSite) {
   if (!base::FeatureList::IsEnabled(features::kUserActivationV2))
     return;
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kBrowserVerifiedUserActivation);
 
   // Start on a page a.com with same-origin iframe on a.com and cross-origin
   // iframe b.com.
diff --git a/chrome/browser/ssl/security_state_tab_helper.cc b/chrome/browser/ssl/security_state_tab_helper.cc
index 12b75d4..87c95a58 100644
--- a/chrome/browser/ssl/security_state_tab_helper.cc
+++ b/chrome/browser/ssl/security_state_tab_helper.cc
@@ -122,22 +122,34 @@
 
 SecurityStateTabHelper::~SecurityStateTabHelper() {}
 
-security_state::SecurityLevel SecurityStateTabHelper::GetSecurityLevel() const {
+security_state::SecurityLevel SecurityStateTabHelper::GetSecurityLevel() {
   return security_state::GetSecurityLevel(
       *GetVisibleSecurityState(), UsedPolicyInstalledCertificate(),
       base::BindRepeating(&content::IsOriginSecure));
 }
 
 std::unique_ptr<security_state::VisibleSecurityState>
-SecurityStateTabHelper::GetVisibleSecurityState() const {
+SecurityStateTabHelper::GetVisibleSecurityState() {
   auto state = security_state::GetVisibleSecurityState(web_contents());
 
   if (state->connection_info_initialized) {
     state->connection_used_legacy_tls =
         IsLegacyTLS(state->url, state->connection_status);
     if (state->connection_used_legacy_tls) {
-      state->is_legacy_tls_control_site =
-          IsTLSDeprecationConfigControlSite(state->url);
+      // We cache the results of the lookup for the duration of a navigation
+      // entry.
+      int navigation_id =
+          web_contents()->GetController().GetVisibleEntry()->GetUniqueID();
+      if (cached_is_legacy_tls_control_site_ &&
+          cached_is_legacy_tls_control_site_.value().first == navigation_id) {
+        state->is_legacy_tls_control_site =
+            cached_is_legacy_tls_control_site_.value().second;
+      } else {
+        state->is_legacy_tls_control_site =
+            IsTLSDeprecationConfigControlSite(state->url);
+        cached_is_legacy_tls_control_site_ = std::pair<int, bool>(
+            navigation_id, state->is_legacy_tls_control_site);
+      }
     }
   }
 
diff --git a/chrome/browser/ssl/security_state_tab_helper.h b/chrome/browser/ssl/security_state_tab_helper.h
index 16e27373..fedf520 100644
--- a/chrome/browser/ssl/security_state_tab_helper.h
+++ b/chrome/browser/ssl/security_state_tab_helper.h
@@ -7,6 +7,7 @@
 
 #include <memory>
 
+#include "base/optional.h"
 #include "components/security_state/core/security_state.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "content/public/browser/web_contents_user_data.h"
@@ -26,9 +27,9 @@
   ~SecurityStateTabHelper() override;
 
   // See security_state::GetSecurityLevel.
-  security_state::SecurityLevel GetSecurityLevel() const;
+  security_state::SecurityLevel GetSecurityLevel();
   std::unique_ptr<security_state::VisibleSecurityState>
-  GetVisibleSecurityState() const;
+  GetVisibleSecurityState();
 
   // content::WebContentsObserver:
   void DidStartNavigation(
@@ -44,6 +45,16 @@
   bool UsedPolicyInstalledCertificate() const;
   security_state::MaliciousContentStatus GetMaliciousContentStatus() const;
 
+  // Caches the legacy TLS control site status for the duration of a page load
+  // (bound to a specific navigation ID) to ensure that we show consistent
+  // security UI (e.g., security indicator and page info). This is because the
+  // control site status depends on external state (a component loading from
+  // disk), which can cause inconsistent state across a page load if it isn't
+  // cached.
+  base::Optional<std::pair<int /* navigation entry ID */,
+                           bool /* is_legacy_tls_control_site */>>
+      cached_is_legacy_tls_control_site_;
+
   WEB_CONTENTS_USER_DATA_KEY_DECL();
 
   DISALLOW_COPY_AND_ASSIGN(SecurityStateTabHelper);
diff --git a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
index 32b5984..d1b82e7 100644
--- a/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
+++ b/chrome/browser/ssl/security_state_tab_helper_browsertest.cc
@@ -1779,6 +1779,11 @@
 const int kObsoleteTLSVersion = net::SSL_CONNECTION_VERSION_TLS1_1;
 // ECDHE_RSA + AES_128_CBC with HMAC-SHA1
 const uint16_t kObsoleteCipherSuite = 0xc013;
+// SHA-256 hash of kMockNonsecureHostname for use in setting a control site in
+// the LegacyTLSExperimentConfig for Legacy TLS tests. Generated with
+// `echo -n "example-nonsecure.test" | openssl sha256`.
+const char kMockControlSiteHash[] =
+    "aaa334d67e96314a14d5679b2309e72f96bf30f9fe9b218e5db3d57be8baa94c";
 
 class BrowserTestNonsecureURLRequest : public InProcessBrowserTest {
  public:
@@ -1897,9 +1902,7 @@
   auto config =
       std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
   GURL control_site(std::string("https://") + kMockNonsecureHostname);
-  std::string control_site_hash =
-      "aaa334d67e96314a14d5679b2309e72f96bf30f9fe9b218e5db3d57be8baa94c";
-  config->add_control_site_hashes(control_site_hash);
+  config->add_control_site_hashes(kMockControlSiteHash);
   SetRemoteTLSDeprecationConfigProto(std::move(config));
 
   auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
@@ -1946,6 +1949,44 @@
                                               original_pref);
 }
 
+// Tests that a page has consistent security state despite the config proto
+// getting loaded during the page visit lifetime (see crbug.com/1011089).
+IN_PROC_BROWSER_TEST_F(BrowserTestNonsecureURLRequest,
+                       LegacyTLSDelayedConfigLoad) {
+  base::test::ScopedFeatureList scoped_feature_list;
+  scoped_feature_list.InitAndEnableFeature(
+      security_state::features::kLegacyTLSWarnings);
+
+  // Navigate to an affected page before the config proto has been set.
+  auto* web_contents = browser()->tab_strip_model()->GetActiveWebContents();
+  auto* helper = SecurityStateTabHelper::FromWebContents(web_contents);
+
+  GURL control_site(std::string("https://") + kMockNonsecureHostname);
+  ui_test_utils::NavigateToURL(browser(), control_site);
+
+  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
+  EXPECT_FALSE(helper->GetVisibleSecurityState()->is_legacy_tls_control_site);
+  EXPECT_EQ(helper->GetSecurityLevel(), security_state::WARNING);
+
+  // Set the config proto.
+  auto config =
+      std::make_unique<chrome_browser_ssl::LegacyTLSExperimentConfig>();
+  config->add_control_site_hashes(kMockControlSiteHash);
+  SetRemoteTLSDeprecationConfigProto(std::move(config));
+
+  // Security state for the current page should not change.
+  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
+  EXPECT_FALSE(helper->GetVisibleSecurityState()->is_legacy_tls_control_site);
+  EXPECT_EQ(helper->GetSecurityLevel(), security_state::WARNING);
+
+  // Refreshing the page should update the page's security state to now be
+  // treated as control group.
+  ui_test_utils::NavigateToURL(browser(), control_site);
+  EXPECT_TRUE(helper->GetVisibleSecurityState()->connection_used_legacy_tls);
+  EXPECT_TRUE(helper->GetVisibleSecurityState()->is_legacy_tls_control_site);
+  EXPECT_EQ(helper->GetSecurityLevel(), security_state::SECURE);
+}
+
 // Tests that the Not Secure chip does not show for error pages on http:// URLs.
 // Regression test for https://crbug.com/760647.
 IN_PROC_BROWSER_TEST_F(SecurityStateTabHelperIncognitoTest, HttpErrorPage) {
diff --git a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
index 389e966..ebbb2efb 100644
--- a/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
+++ b/chrome/browser/sync/test/integration/single_client_secondary_account_sync_test.cc
@@ -7,6 +7,7 @@
 #include "base/macros.h"
 #include "base/path_service.h"
 #include "base/test/scoped_feature_list.h"
+#include "base/threading/thread_restrictions.h"
 #include "build/build_config.h"
 #include "chrome/browser/defaults.h"
 #include "chrome/browser/profiles/profile.h"
@@ -193,14 +194,8 @@
 // The unconsented primary account (aka secondary account) isn't supported on
 // ChromeOS, see IdentityManager::ComputeUnconsentedPrimaryAccountInfo().
 #if !defined(OS_CHROMEOS)
-// Flaky on ASan/TSan, crbug.com/1004312.
-#if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER)
-#define MAYBE_ReusesSameCacheGuid DISABLED_ReusesSameCacheGuid
-#else
-#define MAYBE_ReusesSameCacheGuid ReusesSameCacheGuid
-#endif
 IN_PROC_BROWSER_TEST_F(SingleClientSecondaryAccountSyncTest,
-                       MAYBE_ReusesSameCacheGuid) {
+                       ReusesSameCacheGuid) {
   ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
   ASSERT_TRUE(GetClient(0)->AwaitSyncTransportActive());
 
@@ -214,6 +209,7 @@
   ASSERT_FALSE(prefs.GetCacheGuid().empty());
 
   std::string old_cache_guid;
+  base::ScopedAllowBlockingForTesting allow_blocking;
   ASSERT_TRUE(
       base::ReadFileToString(GetTestFilePathForCacheGuid(), &old_cache_guid));
   ASSERT_FALSE(old_cache_guid.empty());
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.cc b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
index b6fa7fe..f2d1c16 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon.cc
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon.cc
@@ -15,6 +15,7 @@
 #include "base/files/file_util.h"
 #include "base/lazy_instance.h"
 #include "base/macros.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/task/post_task.h"
 #include "chrome/browser/image_decoder.h"
 #include "chrome/browser/ui/app_list/arc/arc_app_list_prefs.h"
@@ -267,7 +268,8 @@
 
   const std::vector<ui::ScaleFactor>& scale_factors =
       ui::GetSupportedScaleFactors();
-  incomplete_scale_factors_.insert(scale_factors.begin(), scale_factors.end());
+  for (const auto& scale_factor : scale_factors)
+    incomplete_scale_factors_.insert({scale_factor, base::Time::Now()});
 }
 
 ArcAppIcon::~ArcAppIcon() {
@@ -276,13 +278,14 @@
 void ArcAppIcon::LoadSupportedScaleFactors() {
   if (serve_compressed_icons_) {
     for (auto scale_factor : incomplete_scale_factors_)
-      LoadForScaleFactor(scale_factor);
+      LoadForScaleFactor(scale_factor.first);
   } else {
     // Calling GetRepresentation indirectly calls LoadForScaleFactor but also
     // first initializes image_skia_ with the placeholder icons (e.g.
     // IDR_APP_DEFAULT_ICON), via ArcAppIcon::Source::GetImageForScale.
     for (auto scale_factor : incomplete_scale_factors_)
-      image_skia_.GetRepresentation(ui::GetScaleForScaleFactor(scale_factor));
+      image_skia_.GetRepresentation(
+          ui::GetScaleForScaleFactor(scale_factor.first));
   }
 }
 
@@ -418,8 +421,16 @@
   image_skia_.AddRepresentation(image_rep);
   image_skia_.RemoveUnsupportedRepresentationsForScale(image_rep.scale());
 
+  if (icon_loaded_count_++ < 5) {
+    base::UmaHistogramTimes(
+        "Arc.IconLoadFromFileTime.uncompressedFirst5",
+        base::Time::Now() - incomplete_scale_factors_[scale_factor]);
+  } else {
+    base::UmaHistogramTimes(
+        "Arc.IconLoadFromFileTime.uncompressedOthers",
+        base::Time::Now() - incomplete_scale_factors_[scale_factor]);
+  }
   incomplete_scale_factors_.erase(scale_factor);
-
   observer_->OnIconUpdated(this);
 }
 
@@ -427,6 +438,16 @@
                                   std::string data) {
   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
   compressed_images_[scale_factor] = std::move(data);
+
+  if (icon_loaded_count_++ < 5) {
+    base::UmaHistogramTimes(
+        "Arc.IconLoadFromFileTime.compressedFirst5",
+        base::Time::Now() - incomplete_scale_factors_[scale_factor]);
+  } else {
+    base::UmaHistogramTimes(
+        "Arc.IconLoadFromFileTime.compressedOthers",
+        base::Time::Now() - incomplete_scale_factors_[scale_factor]);
+  }
   incomplete_scale_factors_.erase(scale_factor);
   observer_->OnIconUpdated(this);
 }
diff --git a/chrome/browser/ui/app_list/arc/arc_app_icon.h b/chrome/browser/ui/app_list/arc/arc_app_icon.h
index bd1fab0..a3be2d1 100644
--- a/chrome/browser/ui/app_list/arc/arc_app_icon.h
+++ b/chrome/browser/ui/app_list/arc/arc_app_icon.h
@@ -133,10 +133,14 @@
   const int resource_size_in_dip_;
   Observer* const observer_;
   const bool serve_compressed_icons_;
+  // Used to separate first 5 loaded app icons and other app icons.
+  // Only one form of app icons will be loaded, compressed or uncompressed, so
+  // only one counter is needed.
+  int icon_loaded_count_ = 0;
 
   gfx::ImageSkia image_skia_;
   std::map<ui::ScaleFactor, std::string> compressed_images_;
-  std::set<ui::ScaleFactor> incomplete_scale_factors_;
+  std::map<ui::ScaleFactor, base::Time> incomplete_scale_factors_;
 
   // Contains pending image decode requests.
   std::vector<std::unique_ptr<DecodeRequest>> decode_requests_;
diff --git a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
index 6f7e108..525a58b 100644
--- a/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_bubble_model.cc
@@ -428,7 +428,7 @@
           prefs, sync_service)) {
     interaction_keeper_->ReportInteractions(this);
     title_ =
-        l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_CONFIRM_SAVED_TITLE);
+        l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_SYNC_PROMO_TITLE);
     state_ = password_manager::ui::CHROME_SIGN_IN_PROMO_STATE;
     int show_count = prefs->GetInteger(
         password_manager::prefs::kNumberSignInPasswordPromoShown);
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image.cc b/chrome/browser/ui/thumbnails/thumbnail_image.cc
index b21932e..bc126c1 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_image.cc
@@ -74,6 +74,11 @@
   ConvertJPEGDataToImageSkiaAndNotifyObservers();
 }
 
+void ThumbnailImage::RequestCompressedThumbnailData() {
+  if (data_)
+    NotifyCompressedDataObservers(data_);
+}
+
 void ThumbnailImage::AssignJPEGData(std::vector<uint8_t> data) {
   data_ = base::MakeRefCounted<base::RefCountedData<std::vector<uint8_t>>>(
       std::move(data));
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image.h b/chrome/browser/ui/thumbnails/thumbnail_image.h
index 033b85a..b6da7b00 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image.h
+++ b/chrome/browser/ui/thumbnails/thumbnail_image.h
@@ -64,7 +64,12 @@
   // Requests that a thumbnail image be made available to observers. Does not
   // guarantee that Observer::OnThumbnailImageAvailable() will be called, or how
   // long it will take, though in most cases it should happen very quickly.
-  virtual void RequestThumbnailImage();
+  void RequestThumbnailImage();
+
+  // Similar to RequestThumbnailImage() but requests only the compressed JPEG
+  // data. Users should listen for a call to
+  // Observer::OnCompressedThumbnailDataAvailable().
+  void RequestCompressedThumbnailData();
 
   void set_async_operation_finished_callback_for_testing(
       base::RepeatingClosure callback) {
diff --git a/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc b/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
index b6afbd69..93b7ed7c 100644
--- a/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
+++ b/chrome/browser/ui/thumbnails/thumbnail_image_unittest.cc
@@ -4,6 +4,7 @@
 
 #include "chrome/browser/ui/thumbnails/thumbnail_image.h"
 
+#include <memory>
 #include <utility>
 
 #include "base/bind.h"
@@ -265,3 +266,17 @@
   EXPECT_EQ(gfx::Size(kTestBitmapWidth, kTestBitmapHeight),
             observer2.thumbnail_image().size());
 }
+
+TEST_F(ThumbnailImageTest, RequestCompressedThumbnailData) {
+  auto image = base::MakeRefCounted<ThumbnailImage>(this);
+  TestThumbnailImageObserver observer;
+  observer.scoped_observer()->Add(image.get());
+
+  SkBitmap bitmap = CreateBitmap(kTestBitmapHeight, kTestBitmapHeight);
+  image->AssignSkBitmap(bitmap);
+  observer.WaitForImage();
+
+  const int count_before_request = observer.new_compressed_count();
+  image->RequestCompressedThumbnailData();
+  EXPECT_EQ(count_before_request + 1, observer.new_compressed_count());
+}
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
index 3f3583c8..4352e40 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/ui/views/frame/webui_tab_strip_container_view.h"
 
+#include <utility>
+
 #include "base/logging.h"
 #include "chrome/app/chrome_command_ids.h"
 #include "chrome/app/vector_icons/vector_icons.h"
@@ -22,6 +24,7 @@
 #include "ui/gfx/color_palette.h"
 #include "ui/gfx/paint_vector_icon.h"
 #include "ui/views/background.h"
+#include "ui/views/controls/menu/menu_runner.h"
 #include "ui/views/controls/webview/webview.h"
 #include "ui/views/layout/fill_layout.h"
 #include "ui/views/layout/flex_layout.h"
@@ -48,9 +51,11 @@
 
   TabStripUI* tab_strip_ui = static_cast<TabStripUI*>(
       web_view_->GetWebContents()->GetWebUI()->GetController());
-  tab_strip_ui->Initialize(browser_);
+  tab_strip_ui->Initialize(browser_, this);
 }
 
+WebUITabStripContainerView::~WebUITabStripContainerView() = default;
+
 views::NativeViewHost* WebUITabStripContainerView::GetNativeViewHost() {
   return web_view_->holder();
 }
@@ -75,6 +80,19 @@
   return toggle_button;
 }
 
+void WebUITabStripContainerView::ShowContextMenuAtPoint(
+    gfx::Point point,
+    std::unique_ptr<ui::MenuModel> menu_model) {
+  ConvertPointToScreen(this, &point);
+  context_menu_model_ = std::move(menu_model);
+  context_menu_runner_ = std::make_unique<views::MenuRunner>(
+      context_menu_model_.get(),
+      views::MenuRunner::HAS_MNEMONICS | views::MenuRunner::CONTEXT_MENU);
+  context_menu_runner_->RunMenuAt(
+      GetWidget(), nullptr, gfx::Rect(point, gfx::Size()),
+      views::MenuAnchorPosition::kTopLeft, ui::MENU_SOURCE_MOUSE);
+}
+
 int WebUITabStripContainerView::GetHeightForWidth(int w) const {
   constexpr int kWebUITabStripHeightDp = 248;
   return kWebUITabStripHeightDp;
diff --git a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
index 9af0ddf..586da13 100644
--- a/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
+++ b/chrome/browser/ui/views/frame/webui_tab_strip_container_view.h
@@ -8,6 +8,7 @@
 #include <memory>
 
 #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
+#include "chrome/browser/ui/webui/tab_strip/tab_strip_ui.h"
 #include "chrome/common/buildflags.h"
 #include "ui/views/view.h"
 
@@ -15,17 +16,24 @@
 #error
 #endif
 
+namespace ui {
+class MenuModel;
+}  // namespace ui
+
 namespace views {
+class MenuRunner;
 class NativeViewHost;
 class WebView;
 }  // namespace views
 
 class Browser;
 
-class WebUITabStripContainerView : public views::View,
+class WebUITabStripContainerView : public TabStripUI::Embedder,
+                                   public views::View,
                                    public views::ButtonListener {
  public:
   explicit WebUITabStripContainerView(Browser* browser);
+  ~WebUITabStripContainerView() override;
 
   views::NativeViewHost* GetNativeViewHost();
 
@@ -34,6 +42,11 @@
   std::unique_ptr<ToolbarButton> CreateToggleButton();
 
  private:
+  // TabStripUI::Embedder:
+  void ShowContextMenuAtPoint(
+      gfx::Point point,
+      std::unique_ptr<ui::MenuModel> menu_model) override;
+
   // views::View:
   int GetHeightForWidth(int w) const override;
 
@@ -42,6 +55,9 @@
 
   Browser* const browser_;
   views::WebView* const web_view_;
+
+  std::unique_ptr<views::MenuRunner> context_menu_runner_;
+  std::unique_ptr<ui::MenuModel> context_menu_model_;
 };
 
 #endif  // CHROME_BROWSER_UI_VIEWS_FRAME_WEBUI_TAB_STRIP_CONTAINER_VIEW_H_
diff --git a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
index 1f4ea21..e09d9ba 100644
--- a/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
+++ b/chrome/browser/ui/views/intent_picker_bubble_view_browsertest.cc
@@ -25,9 +25,8 @@
  public:
   void SetUp() override {
     // TODO(schenney): Stop disabling Paint Holding. crbug.com/1001189
-    scoped_feature_list_.InitWithFeatures(
-        {features::kIntentPicker},
-        {blink::features::kAvoidFlashBetweenNavigation});
+    scoped_feature_list_.InitWithFeatures({features::kIntentPicker},
+                                          {blink::features::kPaintHolding});
     extensions::test::BookmarkAppNavigationBrowserTest::SetUp();
   }
 
diff --git a/chrome/browser/ui/views/page_info/OWNERS b/chrome/browser/ui/views/page_info/OWNERS
index 72c5daf..312e091 100644
--- a/chrome/browser/ui/views/page_info/OWNERS
+++ b/chrome/browser/ui/views/page_info/OWNERS
@@ -1,4 +1,5 @@
 file://chrome/browser/ui/page_info/OWNERS
+per-file safety_tip*=jdeblasio@chromium.org
 
 # COMPONENT: UI>Browser>Bubbles>PageInfo
 # TEAM: security-enamel@chromium.org
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.cc b/chrome/browser/ui/views/profiles/profile_menu_view.cc
index 0cc9528..16d59d8e 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.cc
@@ -176,7 +176,7 @@
   if (profile->IsRegularProfile()) {
     BuildIdentity();
     BuildSyncInfo();
-    BuildAccountFeatureButtons();
+    BuildFeatureButtons();
     BuildAutofillButtons();
   } else if (profile->IsIncognitoProfile()) {
     BuildIncognitoIdentity();
@@ -186,9 +186,9 @@
     NOTREACHED();
   }
 
-  BuildProfileHeading();
+  BuildProfileManagementHeading();
   BuildSelectableProfiles();
-  BuildProfileFeatureButtons();
+  BuildProfileManagementFeatureButtons();
 }
 
 void ProfileMenuView::OnAvatarMenuChanged(
@@ -416,15 +416,16 @@
   base::Optional<AccountInfo> account_info =
       identity_manager->FindExtendedAccountInfoForAccountWithRefreshToken(
           account);
+  ProfileAttributesEntry* profile_attributes =
+      GetProfileAttributesEntry(profile);
 
   if (account_info.has_value()) {
+    SetHeading(profile_attributes->GetLocalProfileName());
     SetIdentityInfo(account_info.value().account_image.AsImageSkia(),
                     GetSyncIcon(),
                     base::UTF8ToUTF16(account_info.value().full_name),
                     base::UTF8ToUTF16(account_info.value().email));
   } else {
-    ProfileAttributesEntry* profile_attributes =
-        GetProfileAttributesEntry(profile);
     SetIdentityInfo(
         profile_attributes->GetAvatarIcon().AsImageSkia(), GetSyncIcon(),
         profile_attributes->GetName(),
@@ -563,13 +564,13 @@
   }
 }
 
-void ProfileMenuView::BuildAccountFeatureButtons() {
+void ProfileMenuView::BuildFeatureButtons() {
   signin::IdentityManager* identity_manager =
       IdentityManagerFactory::GetForProfile(browser()->profile());
   if (!identity_manager->HasUnconsentedPrimaryAccount())
     return;
 
-  AddAccountFeatureButton(
+  AddFeatureButton(
 #if defined(GOOGLE_CHROME_BUILD)
       // The Google G icon needs to be shrunk, so it won't look too big
       // compared to the other icons.
@@ -583,7 +584,7 @@
 
   if (!identity_manager->HasPrimaryAccount()) {
     // The sign-out button is only shown when sync is off.
-    AddAccountFeatureButton(
+    AddFeatureButton(
         ImageForMenu(kSignOutIcon),
         l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT),
         base::BindRepeating(&ProfileMenuView::OnSignoutButtonClicked,
@@ -591,8 +592,8 @@
   }
 }
 
-void ProfileMenuView::BuildProfileHeading() {
-  SetProfileHeading(
+void ProfileMenuView::BuildProfileManagementHeading() {
+  SetProfileManagementHeading(
       l10n_util::GetStringUTF16(IDS_PROFILES_OTHER_PROFILES_TITLE));
 }
 
@@ -620,14 +621,14 @@
   }
 }
 
-void ProfileMenuView::BuildProfileFeatureButtons() {
-  AddProfileShortcutFeatureButton(
+void ProfileMenuView::BuildProfileManagementFeatureButtons() {
+  AddProfileManagementShortcutFeatureButton(
       ImageForMenu(vector_icons::kSettingsIcon, kShortcutIconToImageRatio),
       l10n_util::GetStringUTF16(IDS_PROFILES_MANAGE_USERS_BUTTON),
       base::BindRepeating(&ProfileMenuView::OnManageProfilesButtonClicked,
                           base::Unretained(this)));
 
-  AddProfileFeatureButton(
+  AddProfileManagementFeatureButton(
       ImageForMenu(kAddIcon, /*icon_to_image_ratio=*/0.75),
       l10n_util::GetStringUTF16(IDS_ADD),
       base::BindRepeating(&ProfileMenuView::OnAddNewProfileButtonClicked,
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view.h b/chrome/browser/ui/views/profiles/profile_menu_view.h
index b30d79b..e28e8af 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view.h
@@ -108,10 +108,10 @@
   gfx::ImageSkia GetSyncIcon();
   void BuildAutofillButtons();
   void BuildSyncInfo();
-  void BuildAccountFeatureButtons();
-  void BuildProfileHeading();
+  void BuildFeatureButtons();
+  void BuildProfileManagementHeading();
   void BuildSelectableProfiles();
-  void BuildProfileFeatureButtons();
+  void BuildProfileManagementFeatureButtons();
 
   // Adds the profile chooser view.
   void AddProfileMenuView(AvatarMenu* avatar_menu);
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index 8f831ea2..1de09225 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -274,6 +274,27 @@
   DCHECK(menu_item_groups_.empty());
 }
 
+void ProfileMenuViewBase::SetHeading(const base::string16& heading) {
+  constexpr int kVerticalPadding = 8;
+  const SkColor kBackgroundColor =
+      ui::NativeTheme::GetInstanceForNativeUi()->GetSystemColor(
+          ui::NativeTheme::kColorId_HighlightedMenuItemBackgroundColor);
+  const int kCornerRadius =
+      views::LayoutProvider::Get()->GetCornerRadiusMetric(views::EMPHASIS_HIGH);
+
+  heading_container_->RemoveAllChildViews(/*delete_children=*/true);
+  heading_container_->SetLayoutManager(std::make_unique<views::FillLayout>());
+  heading_container_->SetBackground(
+      views::CreateRoundedRectBackground(kBackgroundColor, kCornerRadius));
+
+  views::Label* label =
+      heading_container_->AddChildView(std::make_unique<views::Label>(
+          heading, views::style::CONTEXT_LABEL, STYLE_HINT));
+  label->SetHandlesTooltips(false);
+  label->SetBorder(
+      views::CreateEmptyBorder(gfx::Insets(kVerticalPadding, kMenuEdgeMargin)));
+}
+
 void ProfileMenuViewBase::SetIdentityInfo(const gfx::ImageSkia& image,
                                           const gfx::ImageSkia& badge,
                                           const base::string16& title,
@@ -397,35 +418,35 @@
   RegisterClickAction(button, std::move(action));
 }
 
-void ProfileMenuViewBase::AddAccountFeatureButton(
-    const gfx::ImageSkia& icon,
-    const base::string16& text,
-    base::RepeatingClosure action) {
+void ProfileMenuViewBase::AddFeatureButton(const gfx::ImageSkia& icon,
+                                           const base::string16& text,
+                                           base::RepeatingClosure action) {
   constexpr int kIconSize = 16;
 
   // Initialize layout if this is the first time a button is added.
-  if (!account_features_container_->GetLayoutManager()) {
-    account_features_container_->SetLayoutManager(
-        std::make_unique<views::BoxLayout>(
-            views::BoxLayout::Orientation::kVertical));
+  if (!features_container_->GetLayoutManager()) {
+    features_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
+        views::BoxLayout::Orientation::kVertical));
   }
 
   views::Button* button =
-      account_features_container_->AddChildView(std::make_unique<HoverButton>(
+      features_container_->AddChildView(std::make_unique<HoverButton>(
           this, SizeImage(ColorImage(icon, GetDefaultIconColor()), kIconSize),
           text));
 
   RegisterClickAction(button, std::move(action));
 }
 
-void ProfileMenuViewBase::SetProfileHeading(const base::string16& heading) {
-  profile_heading_container_->RemoveAllChildViews(/*delete_children=*/true);
-  profile_heading_container_->SetLayoutManager(
+void ProfileMenuViewBase::SetProfileManagementHeading(
+    const base::string16& heading) {
+  profile_mgmt_heading_container_->RemoveAllChildViews(
+      /*delete_children=*/true);
+  profile_mgmt_heading_container_->SetLayoutManager(
       std::make_unique<views::FillLayout>());
 
-  views::Label* label =
-      profile_heading_container_->AddChildView(std::make_unique<views::Label>(
-          heading, views::style::CONTEXT_LABEL, STYLE_HINT));
+  views::Label* label = profile_mgmt_heading_container_->AddChildView(
+      std::make_unique<views::Label>(heading, views::style::CONTEXT_LABEL,
+                                     STYLE_HINT));
   label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   label->SetHandlesTooltips(false);
   label->SetBorder(views::CreateEmptyBorder(gfx::Insets(0, kMenuEdgeMargin)));
@@ -452,38 +473,39 @@
   RegisterClickAction(button, std::move(action));
 }
 
-void ProfileMenuViewBase::AddProfileShortcutFeatureButton(
+void ProfileMenuViewBase::AddProfileManagementShortcutFeatureButton(
     const gfx::ImageSkia& icon,
     const base::string16& text,
     base::RepeatingClosure action) {
   // Initialize layout if this is the first time a button is added.
-  if (!profile_shortcut_features_container_->GetLayoutManager()) {
-    profile_shortcut_features_container_->SetLayoutManager(
+  if (!profile_mgmt_shortcut_features_container_->GetLayoutManager()) {
+    profile_mgmt_shortcut_features_container_->SetLayoutManager(
         CreateBoxLayout(views::BoxLayout::Orientation::kHorizontal,
                         views::BoxLayout::CrossAxisAlignment::kCenter,
                         gfx::Insets(0, 0, 0, /*right=*/kMenuEdgeMargin)));
   }
 
-  views::Button* button = profile_shortcut_features_container_->AddChildView(
-      CreateCircularImageButton(this, icon, text));
+  views::Button* button =
+      profile_mgmt_shortcut_features_container_->AddChildView(
+          CreateCircularImageButton(this, icon, text));
 
   RegisterClickAction(button, std::move(action));
 }
 
-void ProfileMenuViewBase::AddProfileFeatureButton(
+void ProfileMenuViewBase::AddProfileManagementFeatureButton(
     const gfx::ImageSkia& icon,
     const base::string16& text,
     base::RepeatingClosure action) {
   constexpr int kIconSize = 22;
 
   // Initialize layout if this is the first time a button is added.
-  if (!profile_features_container_->GetLayoutManager()) {
-    profile_features_container_->SetLayoutManager(
+  if (!profile_mgmt_features_container_->GetLayoutManager()) {
+    profile_mgmt_features_container_->SetLayoutManager(
         std::make_unique<views::BoxLayout>(
             views::BoxLayout::Orientation::kVertical));
   }
 
-  views::Button* button = profile_features_container_->AddChildView(
+  views::Button* button = profile_mgmt_features_container_->AddChildView(
       std::make_unique<HoverButton>(this, SizeImage(icon, kIconSize), text));
 
   RegisterClickAction(button, std::move(action));
@@ -597,31 +619,33 @@
   // Create and add new component containers in the correct order.
   // First, add the bordered box with the identity and feature buttons.
   views::View* bordered_box = components->AddChildView(CreateBorderedBoxView());
+  heading_container_ =
+      bordered_box->AddChildView(std::make_unique<views::View>());
   identity_info_container_ =
       bordered_box->AddChildView(std::make_unique<views::View>());
   shortcut_features_container_ =
       bordered_box->AddChildView(std::make_unique<views::View>());
   sync_info_container_ =
       bordered_box->AddChildView(std::make_unique<views::View>());
-  account_features_container_ =
+  features_container_ =
       bordered_box->AddChildView(std::make_unique<views::View>());
   // Second, add the profile header.
   auto profile_header = std::make_unique<views::View>();
   views::BoxLayout* profile_header_layout =
       profile_header->SetLayoutManager(std::make_unique<views::BoxLayout>(
           views::BoxLayout::Orientation::kHorizontal));
-  profile_heading_container_ =
+  profile_mgmt_heading_container_ =
       profile_header->AddChildView(std::make_unique<views::View>());
-  profile_header_layout->SetFlexForView(profile_heading_container_, 1);
-  profile_shortcut_features_container_ =
+  profile_header_layout->SetFlexForView(profile_mgmt_heading_container_, 1);
+  profile_mgmt_shortcut_features_container_ =
       profile_header->AddChildView(std::make_unique<views::View>());
-  profile_header_layout->SetFlexForView(profile_shortcut_features_container_,
-                                        0);
+  profile_header_layout->SetFlexForView(
+      profile_mgmt_shortcut_features_container_, 0);
   components->AddChildView(std::move(profile_header));
   // Third, add the profile buttons at the bottom.
   selectable_profiles_container_ =
       components->AddChildView(std::make_unique<views::View>());
-  profile_features_container_ =
+  profile_mgmt_features_container_ =
       components->AddChildView(std::make_unique<views::View>());
 
   // Create a scroll view to hold the components.
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.h b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
index 5a9a59d7..674221c 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
@@ -95,6 +95,7 @@
   virtual void BuildMenu() = 0;
 
   // API to build the profile menu.
+  void SetHeading(const base::string16& heading);
   void SetIdentityInfo(const gfx::ImageSkia& image,
                        const gfx::ImageSkia& badge,
                        const base::string16& title,
@@ -106,19 +107,19 @@
   void AddShortcutFeatureButton(const gfx::ImageSkia& icon,
                                 const base::string16& text,
                                 base::RepeatingClosure action);
-  void AddAccountFeatureButton(const gfx::ImageSkia& icon,
-                               const base::string16& text,
-                               base::RepeatingClosure action);
-  void SetProfileHeading(const base::string16& heading);
+  void AddFeatureButton(const gfx::ImageSkia& icon,
+                        const base::string16& text,
+                        base::RepeatingClosure action);
+  void SetProfileManagementHeading(const base::string16& heading);
   void AddSelectableProfile(const gfx::ImageSkia& image,
                             const base::string16& name,
                             base::RepeatingClosure action);
-  void AddProfileShortcutFeatureButton(const gfx::ImageSkia& icon,
-                                       const base::string16& text,
-                                       base::RepeatingClosure action);
-  void AddProfileFeatureButton(const gfx::ImageSkia& icon,
-                               const base::string16& text,
-                               base::RepeatingClosure action);
+  void AddProfileManagementShortcutFeatureButton(const gfx::ImageSkia& icon,
+                                                 const base::string16& text,
+                                                 base::RepeatingClosure action);
+  void AddProfileManagementFeatureButton(const gfx::ImageSkia& icon,
+                                         const base::string16& text,
+                                         base::RepeatingClosure action);
   // 0 < |icon_to_image_ratio| <= 1 is the size ratio of |icon| in the returned
   // image. E.g. a value of 0.8 means that |icon| only takes up 80% of the
   // returned image, with the rest being padding around it.
@@ -227,14 +228,15 @@
   std::map<views::View*, base::RepeatingClosure> click_actions_;
 
   // Component containers.
+  views::View* heading_container_ = nullptr;
   views::View* identity_info_container_ = nullptr;
   views::View* sync_info_container_ = nullptr;
   views::View* shortcut_features_container_ = nullptr;
-  views::View* account_features_container_ = nullptr;
-  views::View* profile_heading_container_ = nullptr;
+  views::View* features_container_ = nullptr;
+  views::View* profile_mgmt_heading_container_ = nullptr;
   views::View* selectable_profiles_container_ = nullptr;
-  views::View* profile_shortcut_features_container_ = nullptr;
-  views::View* profile_features_container_ = nullptr;
+  views::View* profile_mgmt_shortcut_features_container_ = nullptr;
+  views::View* profile_mgmt_features_container_ = nullptr;
 
   CloseBubbleOnTabActivationHelper close_bubble_helper_;
 
diff --git a/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc b/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc
index 3779374..66b5e47 100644
--- a/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc
+++ b/chrome/browser/ui/views/sharing/sharing_dialog_view_unittest.cc
@@ -72,6 +72,7 @@
           base::StrCat({"device_guid_", base::NumberToString(i)}),
           base::StrCat({"device", base::NumberToString(i)}), "chrome_version",
           "user_agent", sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
+          base::SysInfo::HardwareInfo(),
           /*last_updated_timestamp=*/base::Time::Now(),
           /*send_tab_to_self_receiving_enabled=*/false,
           /*sharing_info=*/base::nullopt));
@@ -144,12 +145,13 @@
 }
 
 TEST_F(SharingDialogViewTest, DevicePressed) {
-  syncer::DeviceInfo device_info(
-      "device_guid_1", "device1", "chrome_version", "user_agent",
-      sync_pb::SyncEnums_DeviceType_TYPE_PHONE, "device_id",
-      /*last_updated_timestamp=*/base::Time::Now(),
-      /*send_tab_to_self_receiving_enabled=*/false,
-      /*sharing_info=*/base::nullopt);
+  syncer::DeviceInfo device_info("device_guid_1", "device1", "chrome_version",
+                                 "user_agent",
+                                 sync_pb::SyncEnums_DeviceType_TYPE_PHONE,
+                                 "device_id", base::SysInfo::HardwareInfo(),
+                                 /*last_updated_timestamp=*/base::Time::Now(),
+                                 /*send_tab_to_self_receiving_enabled=*/false,
+                                 /*sharing_info=*/base::nullopt);
   EXPECT_CALL(device_callback, Call(DeviceEquals(&device_info)));
 
   auto dialog_data = CreateDialogData(/*devices=*/3, /*apps=*/2);
diff --git a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
index dbb6736..5b2a655 100644
--- a/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
+++ b/chrome/browser/ui/views/tabs/tab_hover_card_bubble_view.cc
@@ -347,22 +347,22 @@
   layout->SetCrossAxisAlignment(views::LayoutAlignment::kStretch);
   layout->SetCollapseMargins(true);
 
-  constexpr int kHorizontalMargin = 12;
-  constexpr int kVerticalMargin = 18;
+  constexpr int kVerticalMargin = 12;
+  constexpr int kHorizontalMargin = 18;
   constexpr int kLineSpacing = 0;
   title_label_->SetProperty(views::kMarginsKey,
-                            gfx::Insets(kHorizontalMargin, kVerticalMargin,
-                                        kLineSpacing, kVerticalMargin));
+                            gfx::Insets(kVerticalMargin, kHorizontalMargin,
+                                        kLineSpacing, kHorizontalMargin));
   title_label_->SetProperty(views::kFlexBehaviorKey,
                             views::FlexSpecification::ForSizeRule(
                                 views::MinimumFlexSizeRule::kScaleToMinimum,
                                 views::MaximumFlexSizeRule::kPreferred));
   alert_state_label_->SetProperty(views::kMarginsKey,
-                                  gfx::Insets(kLineSpacing, kVerticalMargin,
-                                              kLineSpacing, kVerticalMargin));
+                                  gfx::Insets(kLineSpacing, kHorizontalMargin,
+                                              kLineSpacing, kHorizontalMargin));
   domain_label_->SetProperty(views::kMarginsKey,
-                             gfx::Insets(kLineSpacing, kVerticalMargin,
-                                         kHorizontalMargin, kVerticalMargin));
+                             gfx::Insets(kLineSpacing, kHorizontalMargin,
+                                         kVerticalMargin, kHorizontalMargin));
 
   widget_ = views::BubbleDialogDelegateView::CreateBubble(this);
   set_adjust_if_offscreen(true);
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
index 446fc7d..9a3f688 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.cc
@@ -377,21 +377,34 @@
 
 UserActionBuckets DetermineUserAction(const base::Value& settings) {
 #if defined(OS_MACOSX)
-  if (settings.FindKey(kSettingOpenPDFInPreview) != nullptr)
+  if (settings.FindKey(kSettingOpenPDFInPreview))
     return OPEN_IN_MAC_PREVIEW;
 #endif
   // This needs to be checked before checking for a cloud print ID, since a
   // print ticket for printing to Drive will also contain a cloud print ID.
   if (settings.FindBoolKey(kSettingPrintToGoogleDrive).value_or(false))
     return PRINT_TO_GOOGLE_DRIVE;
-  if (settings.FindKey(kSettingCloudPrintId) != nullptr)
+  if (settings.FindKey(kSettingCloudPrintId))
     return PRINT_WITH_CLOUD_PRINT;
-  if (settings.FindBoolKey(kSettingPrintWithPrivet).value_or(false))
-    return PRINT_WITH_PRIVET;
-  if (settings.FindBoolKey(kSettingPrintWithExtension).value_or(false))
-    return PRINT_WITH_EXTENSION;
-  if (settings.FindBoolKey(kSettingPrintToPDF).value_or(false))
-    return PRINT_TO_PDF;
+
+  base::Optional<int> type_raw = settings.FindIntKey(kSettingPrinterType);
+  if (type_raw.has_value()) {
+    PrinterType type = static_cast<PrinterType>(type_raw.value());
+    switch (type) {
+      case kPrivetPrinter:
+        return PRINT_WITH_PRIVET;
+      case kExtensionPrinter:
+        return PRINT_WITH_EXTENSION;
+      case kPdfPrinter:
+        return PRINT_TO_PDF;
+      case kLocalPrinter:
+        break;
+      default:
+        NOTREACHED();
+        break;
+    }
+  }
+
   if (settings.FindBoolKey(kSettingShowSystemDialog).value_or(false))
     return FALLBACK_TO_ADVANCED_SETTINGS_DIALOG;
   return PRINT_TO_PRINTER;
diff --git a/chrome/browser/ui/webui/print_preview/print_preview_handler.h b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
index bdccac1..e5f5f92 100644
--- a/chrome/browser/ui/webui/print_preview/print_preview_handler.h
+++ b/chrome/browser/ui/webui/print_preview/print_preview_handler.h
@@ -38,16 +38,6 @@
 class PrinterHandler;
 class PrintPreviewUI;
 
-// Must match print_preview.PrinterType in
-// chrome/browser/resources/print_preview/native_layer.js
-enum PrinterType {
-  kPrivetPrinter,
-  kExtensionPrinter,
-  kPdfPrinter,
-  kLocalPrinter,
-  kCloudPrinter
-};
-
 // The handler for Javascript messages related to the print preview dialog.
 class PrintPreviewHandler : public content::WebUIMessageHandler,
                             public signin::IdentityManager::Observer {
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
index 37b43a17..42d0183 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.cc
@@ -18,6 +18,7 @@
 #include "chrome/browser/themes/theme_service.h"
 #include "chrome/browser/themes/theme_service_factory.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/tabs/tab_menu_model.h"
 #include "chrome/browser/ui/tabs/tab_network_state.h"
 #include "chrome/browser/ui/tabs/tab_renderer_data.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
@@ -35,8 +36,10 @@
 #include "content/public/browser/web_ui_message_handler.h"
 #include "third_party/skia/include/core/SkImageEncoder.h"
 #include "third_party/skia/include/core/SkStream.h"
+#include "ui/base/models/simple_menu_model.h"
 #include "ui/base/theme_provider.h"
 #include "ui/gfx/color_utils.h"
+#include "ui/gfx/geometry/point_conversions.h"
 
 namespace {
 
@@ -65,11 +68,32 @@
   std::vector<unsigned char> result_;
 };
 
+class WebUITabContextMenu : public ui::SimpleMenuModel::Delegate,
+                            public TabMenuModel {
+ public:
+  WebUITabContextMenu(TabStripModel* tab_strip_model, int tab_index)
+      : TabMenuModel(this, tab_strip_model, tab_index),
+        tab_strip_model_(tab_strip_model),
+        tab_index_(tab_index) {}
+  ~WebUITabContextMenu() override = default;
+
+  void ExecuteCommand(int command_id, int event_flags) override {
+    DCHECK_LT(tab_index_, tab_strip_model_->count());
+    tab_strip_model_->ExecuteContextMenuCommand(
+        tab_index_, static_cast<TabStripModel::ContextMenuCommand>(command_id));
+  }
+
+ private:
+  TabStripModel* const tab_strip_model_;
+  const int tab_index_;
+};
+
 class TabStripUIHandler : public content::WebUIMessageHandler,
                           public TabStripModelObserver {
  public:
-  explicit TabStripUIHandler(Browser* browser)
+  explicit TabStripUIHandler(Browser* browser, TabStripUI::Embedder* embedder)
       : browser_(browser),
+        embedder_(embedder),
         thumbnail_tracker_(base::Bind(&TabStripUIHandler::HandleThumbnailUpdate,
                                       base::Unretained(this))) {}
   ~TabStripUIHandler() override = default;
@@ -156,6 +180,10 @@
     web_ui()->RegisterMessageCallback(
         "removeTrackedTab", base::Bind(&TabStripUIHandler::RemoveTrackedTab,
                                        base::Unretained(this)));
+    web_ui()->RegisterMessageCallback(
+        "showTabContextMenu",
+        base::Bind(&TabStripUIHandler::HandleShowTabContextMenu,
+                   base::Unretained(this)));
   }
 
  private:
@@ -166,8 +194,6 @@
                         browser_->tab_strip_model()->active_index() == index);
     tab_data.SetInteger("id", extensions::ExtensionTabUtil::GetTabId(contents));
     tab_data.SetInteger("index", index);
-    tab_data.SetString("status", extensions::ExtensionTabUtil::GetTabStatusText(
-                                     contents->IsLoading()));
 
     // TODO(johntlee): Replace with favicon from TabRendererData
     content::NavigationEntry* visible_entry =
@@ -181,6 +207,10 @@
     tab_data.SetBoolean("pinned", tab_renderer_data.pinned);
     tab_data.SetString("title", tab_renderer_data.title);
     tab_data.SetString("url", tab_renderer_data.visible_url.GetContent());
+    tab_data.SetInteger("networkState",
+                        static_cast<int>(tab_renderer_data.network_state));
+    tab_data.SetBoolean("shouldHideThrobber",
+                        tab_renderer_data.should_hide_throbber);
     // TODO(johntlee): Add the rest of TabRendererData
 
     return tab_data;
@@ -225,6 +255,9 @@
     colors.SetString("--tabstrip-tab-loading-spinning-color",
                      color_utils::SkColorToRgbaString(tp.GetColor(
                          ThemeProperties::COLOR_TAB_THROBBER_SPINNING)));
+    colors.SetString("--tabstrip-tab-waiting-spinning-color",
+                     color_utils::SkColorToRgbaString(tp.GetColor(
+                         ThemeProperties::COLOR_TAB_THROBBER_WAITING)));
     colors.SetString("--tabstrip-indicator-recording-color",
                      color_utils::SkColorToRgbaString(tp.GetColor(
                          ThemeProperties::COLOR_TAB_ALERT_RECORDING)));
@@ -238,6 +271,33 @@
     ResolveJavascriptCallback(callback_id, colors);
   }
 
+  void HandleShowTabContextMenu(const base::ListValue* args) {
+    int tab_id = 0;
+    args->GetInteger(0, &tab_id);
+
+    gfx::PointF point;
+    {
+      double x = 0;
+      args->GetDouble(1, &x);
+      double y = 0;
+      args->GetDouble(2, &y);
+      point = gfx::PointF(x, y);
+    }
+
+    TabStripModel* tab_strip_model = nullptr;
+    int tab_index = -1;
+    const bool got_tab = extensions::ExtensionTabUtil::GetTabById(
+        tab_id, browser_->profile(), true /* include_incognito */, nullptr,
+        &tab_strip_model, nullptr, &tab_index);
+    DCHECK(got_tab);
+    DCHECK_EQ(tab_strip_model, browser_->tab_strip_model());
+
+    DCHECK(embedder_);
+    embedder_->ShowContextMenuAtPoint(
+        gfx::ToRoundedPoint(point),
+        std::make_unique<WebUITabContextMenu>(tab_strip_model, tab_index));
+  }
+
   void AddTrackedTab(const base::ListValue* args) {
     AllowJavascript();
 
@@ -296,6 +356,8 @@
   }
 
   Browser* const browser_;
+  TabStripUI::Embedder* const embedder_;
+
   ThumbnailTracker thumbnail_tracker_;
 
   DISALLOW_COPY_AND_ASSIGN(TabStripUIHandler);
@@ -341,6 +403,9 @@
 
 TabStripUI::~TabStripUI() {}
 
-void TabStripUI::Initialize(Browser* browser) {
-  web_ui()->AddMessageHandler(std::make_unique<TabStripUIHandler>(browser));
+void TabStripUI::Initialize(Browser* browser, Embedder* embedder) {
+  content::WebUI* const web_ui = TabStripUI::web_ui();
+  DCHECK_EQ(Profile::FromWebUI(web_ui), browser->profile());
+  web_ui->AddMessageHandler(
+      std::make_unique<TabStripUIHandler>(browser, embedder));
 }
diff --git a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
index c677074..02c375f 100644
--- a/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
+++ b/chrome/browser/ui/webui/tab_strip/tab_strip_ui.h
@@ -5,22 +5,44 @@
 #ifndef CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_UI_H_
 #define CHROME_BROWSER_UI_WEBUI_TAB_STRIP_TAB_STRIP_UI_H_
 
+#include <memory>
+
 #include "base/macros.h"
 #include "chrome/browser/ui/webui/tab_strip/thumbnail_tracker.h"
 #include "content/public/browser/web_ui_controller.h"
 
 class Browser;
 
+namespace gfx {
+class Point;
+}
+
+namespace ui {
+class MenuModel;
+}
+
 // The WebUI version of the tab strip in the browser. It is currently only
 // supported on ChromeOS in tablet mode.
 class TabStripUI : public content::WebUIController {
  public:
+  // Interface to be implemented by the embedder. Provides native UI
+  // functionality such as showing context menus.
+  class Embedder {
+   public:
+    Embedder() = default;
+    virtual ~Embedder() {}
+
+    virtual void ShowContextMenuAtPoint(
+        gfx::Point point,
+        std::unique_ptr<ui::MenuModel> menu_model) = 0;
+  };
+
   explicit TabStripUI(content::WebUI* web_ui);
   ~TabStripUI() override;
 
-  // Initialize TabStripUI with the Browser it is running in. Must be called
-  // exactly once. The WebUI won't work until this is called.
-  void Initialize(Browser* browser);
+  // Initialize TabStripUI with its embedder and the Browser it's running in.
+  // Must be called exactly once. The WebUI won't work until this is called.
+  void Initialize(Browser* browser, Embedder* embedder);
 
  private:
   void HandleThumbnailUpdate(int extension_tab_id, gfx::ImageSkia image);
diff --git a/chrome/common/net/net_resource_provider.cc b/chrome/common/net/net_resource_provider.cc
index 366c894d2..1ee2132 100644
--- a/chrome/common/net/net_resource_provider.cc
+++ b/chrome/common/net/net_resource_provider.cc
@@ -8,7 +8,6 @@
 
 #include "base/i18n/rtl.h"
 #include "base/no_destructor.h"
-#include "base/strings/string_piece.h"
 #include "base/values.h"
 #include "chrome/grit/chromium_strings.h"
 #include "chrome/grit/generated_resources.h"
@@ -23,8 +22,6 @@
 // The net module doesn't have access to this HTML or the strings that need to
 // be localized.  The Chrome locale will never change while we're running, so
 // it's safe to have a static string that we always return a pointer into.
-// This allows us to have the ResourceProvider return a pointer into the actual
-// resource (via a StringPiece), instead of always copying resources.
 struct LazyDirectoryListerCacher {
   LazyDirectoryListerCacher() {
     base::DictionaryValue value;
@@ -44,22 +41,24 @@
         l10n_util::GetStringFUTF16(IDS_DIRECTORY_LISTING_PARSING_ERROR_BOX_TEXT,
             l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)));
     value.SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
-    html_data = webui::GetI18nTemplateHtml(
-        ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
+    std::string str = webui::GetI18nTemplateHtml(
+        ui::ResourceBundle::GetSharedInstance().DecompressDataResource(
             IDR_DIR_HEADER_HTML),
         &value);
+
+    html_data = base::RefCountedString::TakeString(&str);
   }
 
-  std::string html_data;
+  scoped_refptr<base::RefCountedMemory> html_data;
 };
 
 }  // namespace
 
-base::StringPiece ChromeNetResourceProvider(int key) {
+scoped_refptr<base::RefCountedMemory> ChromeNetResourceProvider(int key) {
   static base::NoDestructor<LazyDirectoryListerCacher> lazy_dir_lister;
 
   if (IDR_DIR_HEADER_HTML == key)
-    return base::StringPiece(lazy_dir_lister->html_data);
+    return lazy_dir_lister->html_data;
 
-  return ui::ResourceBundle::GetSharedInstance().GetRawDataResource(key);
+  return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(key);
 }
diff --git a/chrome/common/net/net_resource_provider.h b/chrome/common/net/net_resource_provider.h
index de752f1..7a2d545 100644
--- a/chrome/common/net/net_resource_provider.h
+++ b/chrome/common/net/net_resource_provider.h
@@ -5,9 +5,9 @@
 #ifndef CHROME_COMMON_NET_NET_RESOURCE_PROVIDER_H_
 #define CHROME_COMMON_NET_NET_RESOURCE_PROVIDER_H_
 
-#include "base/strings/string_piece.h"
+#include "base/memory/ref_counted_memory.h"
 
 // This is called indirectly by the network layer to access resources.
-base::StringPiece ChromeNetResourceProvider(int key);
+scoped_refptr<base::RefCountedMemory> ChromeNetResourceProvider(int key);
 
 #endif  // CHROME_COMMON_NET_NET_RESOURCE_PROVIDER_H_
diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc
index c275229..5412c556 100644
--- a/chrome/renderer/searchbox/searchbox_extension.cc
+++ b/chrome/renderer/searchbox/searchbox_extension.cc
@@ -871,13 +871,13 @@
   if (!search_box)
     return v8::Null(isolate);
 
-  content::RenderView* render_view =
-      GetMainRenderFrameForCurrentContext()->GetRenderView();
+  content::RenderFrame* render_frame = GetMainRenderFrameForCurrentContext();
+  content::RenderView* render_view = render_frame->GetRenderView();
 
   // This corresponds to "window.devicePixelRatio" in JavaScript.
   float zoom_factor =
       blink::PageZoomLevelToZoomFactor(render_view->GetZoomLevel());
-  float device_pixel_ratio = render_view->GetDeviceScaleFactor() * zoom_factor;
+  float device_pixel_ratio = render_frame->GetDeviceScaleFactor() * zoom_factor;
 
   int render_view_id = render_view->GetRoutingID();
 
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 3b0f5ae..13a6f569 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -3081,8 +3081,6 @@
     "../browser/page_load_metrics/observers/offline_page_previews_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.cc",
     "../browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h",
-    "../browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc",
-    "../browser/page_load_metrics/observers/page_load_metrics_observer_tester.h",
     "../browser/page_load_metrics/observers/previews_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/previews_ukm_observer_unittest.cc",
     "../browser/page_load_metrics/observers/protocol_page_load_metrics_observer_unittest.cc",
@@ -3093,7 +3091,6 @@
     "../browser/page_load_metrics/observers/tab_restore_page_load_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/third_party_metrics_observer_unittest.cc",
     "../browser/page_load_metrics/observers/ukm_page_load_metrics_observer_unittest.cc",
-    "../browser/page_load_metrics/observers/use_counter_page_load_metrics_observer_unittest.cc",
     "../browser/password_manager/chrome_password_manager_client_unittest.cc",
     "../browser/password_manager/password_store_x_unittest.cc",
     "../browser/payments/payment_handler_permission_context_unittest.cc",
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index da0b934..49c23fa 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -237,9 +237,10 @@
         std::move(window_types), capabilities->page_load_strategy));
   }
 
-  base::TimeTicks deadline =
-      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(wait_time);
-  Status status = client->Init(deadline - base::TimeTicks::Now());
+  const base::TimeTicks initial = base::TimeTicks::Now();
+  const base::TimeTicks deadline =
+      initial + base::TimeDelta::FromSeconds(wait_time);
+  Status status = client->Init(deadline - initial);
   if (status.IsError())
     return status;
 
@@ -281,9 +282,13 @@
     }
   }
 
-  while (base::TimeTicks::Now() < deadline) {
+  // Always try GetWebViewsInfo at least once if the client
+  // initialized successfully.
+  do {
     WebViewsInfo views_info;
-    client->GetWebViewsInfo(&views_info);
+    status = client->GetWebViewsInfo(&views_info);
+    if (status.IsError())
+      return status;
     for (size_t i = 0; i < views_info.GetSize(); ++i) {
       if (views_info.Get(i).type == WebViewInfo::kPage) {
         *user_client = std::move(client);
@@ -291,7 +296,8 @@
       }
     }
     base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
-  }
+  } while (base::TimeTicks::Now() < deadline);
+
   return Status(kUnknownError, "unable to discover open pages");
 }
 
diff --git a/chrome/test/data/autofill/captured_sites/testcases.json b/chrome/test/data/autofill/captured_sites/testcases.json
index 2c41cc7..72af0f6 100644
--- a/chrome/test/data/autofill/captured_sites/testcases.json
+++ b/chrome/test/data/autofill/captured_sites/testcases.json
@@ -33,7 +33,7 @@
     { "site_name": "creditonebank" },
     { "site_name": "cricketwireless" },
     { "site_name": "crutch_field" },
-    { "site_name": "davidsbridal", "disabled":true, "bug_number":1006308 },
+    { "site_name": "davidsbridal" },
     { "site_name": "dickblick" },
     { "site_name": "dish" },
     { "site_name": "eddiebauer" },
@@ -119,7 +119,7 @@
     { "site_name": "belk", "expectation":"PASS" },
     { "site_name": "catch", "expectation":"PASS" },
     { "site_name": "cdbaby", "expectation":"PASS" },
-    { "site_name": "coach", "expectation":"FAIL", "bug_number":996478 },
+    { "site_name": "coach", "expectation":"PASS" },
     { "site_name": "customink", "expectation":"PASS" },
     { "site_name": "dell", "expectation":"PASS", "disabled":true, "bug_number":993044 },
     { "site_name": "fromyouflowers", "expectation":"PASS" },
@@ -140,13 +140,13 @@
     { "site_name": "dicks_sporting_goods", "expectation":"PASS" },
     { "site_name": "duluthtrading", "expectation":"PASS" },
     { "site_name": "fansedge", "expectation":"PASS" },
-    { "site_name": "findmeagift", "expectation":"PASS", "disabled":true, "bug_number":993044 },
+    { "site_name": "findmeagift", "expectation":"PASS" },
     { "site_name": "groupon", "expectation":"PASS" },
     { "site_name": "kohls", "expectation":"PASS", "disabled":true, "bug_number":1006308 },
     { "site_name": "living_social", "expectation":"PASS" },
     { "site_name": "mango", "expectation":"PASS" },
-    { "site_name": "neiman_marcus", "expectation":"FAIL", "bug_number":996478 },
-    { "site_name": "north_face", "expectation":"PASS", "disabled":true, "bug_number":1006308 },
+    { "site_name": "neiman_marcus", "expectation":"PASS" },
+    { "site_name": "north_face", "expectation":"PASS" },
     { "site_name": "oshkosh", "expectation":"FAIL", "bug_number":996478 },
     { "site_name": "partycity", "expectation":"PASS" },
     { "site_name": "payless", "expectation":"PASS" },
@@ -154,26 +154,26 @@
     { "site_name": "represent", "expectation":"PASS" },
     { "site_name": "tillys", "expectation":"PASS" },
     { "site_name": "verizonwireless", "expectation":"PASS", "disabled":true, "bug_number":1006308 },
-    { "site_name": "wiley", "expectation":"PASS", "disabled":true, "bug_number":1003044 },
+    { "site_name": "wiley", "expectation":"PASS" },
     { "site_name": "zenni_optical", "expectation":"PASS" },
     { "site_name": "accesscorrections", "expectation":"PASS" },
     { "site_name": "jetspizza", "expectation":"PASS" },
     { "site_name": "star_city_games", "expectation":"PASS" },
     { "site_name": "1aauto", "expectation":"PASS" },
-    { "site_name": "abercrombie", "expectation":"PASS", "disabled":true, "bug_number":984664 },
+    { "site_name": "abercrombie", "expectation":"FAIL", "disabled":true, "bug_number":984664 },
     { "site_name": "barnes_and_noble", "expectation":"PASS", "disabled":true, "bug_number":984664 },
     { "site_name": "collage", "expectation":"PASS" },
     { "site_name": "cvs", "expectation":"PASS" },
     { "site_name": "ebay", "expectation":"PASS", "disabled":true, "bug_number":984664 },
     { "site_name": "elementvape", "expectation":"PASS", "disabled":true, "bug_number":984664 },
-    { "site_name": "fender", "expectation":"PASS", "disabled":true, "bug_number":984664 },
+    { "site_name": "fender", "expectation":"PASS" },
     { "site_name": "gamestop", "expectation":"PASS" },
     { "site_name": "macys", "expectation":"PASS", "disabled":true, "bug_number":984664 },
     { "site_name": "newegg", "expectation":"PASS" },
     { "site_name": "nordstrom", "expectation":"PASS" },
     { "site_name": "omahasteaks", "expectation":"PASS", "disabled":true, "bug_number":984664 },
     { "site_name": "panerabread", "expectation":"PASS", "disabled":true, "bug_number":984664 },
-    { "site_name": "pets_mart", "expectation":"PASS", "disabled":true, "bug_number":984664 },
+    { "site_name": "pets_mart", "expectation":"PASS" },
     { "site_name": "polar", "expectation":"PASS", "disabled":true, "bug_number":984664 },
     { "site_name": "safishing", "expectation":"PASS" },
     { "site_name": "ticket_master", "expectation":"PASS" },
diff --git a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
index 6d593a9..f497635e 100644
--- a/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
+++ b/chrome/test/data/extensions/api_test/webrequest/test_redirects.js
@@ -115,9 +115,9 @@
       chrome.webRequest.onHeadersReceived.addListener(listener,
           {urls: [url]}, onHeadersReceivedExtraInfoSpec);
 
-      // The page should be redirected to redirectURL, but not to the non web
-      // accessible URL.
-      assertRedirectSucceeds(url, redirectURL, function() {
+      // The page should be redirected to the non web accessible URL, but this
+      // URL will not load.
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
         chrome.webRequest.onHeadersReceived.removeListener(listener);
       });
     },
@@ -208,9 +208,9 @@
       chrome.webRequest.onBeforeRequest.addListener(listener,
           {urls: [url]}, ['blocking']);
 
-      // The page should be redirected to redirectURL, but not to the non web
-      // accessible URL.
-      assertRedirectSucceeds(url, redirectURL, function() {
+      // The page should be redirected to the non web accessible URL, but this
+      // URL will not load.
+      assertRedirectSucceeds(url, getURLNonWebAccessible(), function() {
         chrome.webRequest.onBeforeRequest.removeListener(listener);
       });
     },
@@ -224,8 +224,9 @@
     },
 
     function redirectToNonWebAccessibleUrlWithServerRedirect() {
-      assertRedirectFails(
-          getServerURL('server-redirect?' + getURLNonWebAccessible()));
+      assertRedirectSucceeds(
+          getServerURL('server-redirect?' + getURLNonWebAccessible()),
+          getURLNonWebAccessible());
     },
 
     function redirectToWebAccessibleUrlWithServerRedirect() {
diff --git a/chrome/test/data/pdf/viewport_test.js b/chrome/test/data/pdf/viewport_test.js
index fde77d6..730af982 100644
--- a/chrome/test/data/pdf/viewport_test.js
+++ b/chrome/test/data/pdf/viewport_test.js
@@ -221,6 +221,11 @@
     mockWindow.scrollTo(0, 170);
     chrome.test.assertEq(1, viewport.getMostVisiblePage());
 
+    // Zoom out so that more than one page fits and scroll to the bottom.
+    viewport.setZoom(0.4);
+    mockWindow.scrollTo(0, 160);
+    chrome.test.assertEq(2, viewport.getMostVisiblePage());
+
     // Zoomed out with the entire document visible.
     viewport.setZoom(0.25);
     mockWindow.scrollTo(0, 0);
diff --git a/chrome/test/data/webui/cr_components/network_config_test.js b/chrome/test/data/webui/cr_components/network_config_test.js
index 147701df..a915475 100644
--- a/chrome/test/data/webui/cr_components/network_config_test.js
+++ b/chrome/test/data/webui/cr_components/network_config_test.js
@@ -234,6 +234,10 @@
             'PEAP',
             networkConfig.managedProperties.typeProperties.ethernet.eap.outer
                 .activeValue);
+        assertEquals(
+            'PEAP',
+            networkConfig.configProperties_.typeConfig.ethernet.eap.outer);
+        assertEquals('PEAP', networkConfig.eapProperties_.outer);
         let outer = networkConfig.$$('#outer');
         assertTrue(!!outer);
         assertTrue(!outer.disabled);
diff --git a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
index 5e867c2..8acaa03 100644
--- a/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
+++ b/chrome/test/data/webui/extensions/cr_extensions_browsertest.js
@@ -398,13 +398,7 @@
   this.runMochaTest(extension_item_list_tests.TestNames.Filtering);
 });
 
-// This test is flaky on Mac10.9 Tests (dbg). See https://crbug.com/771099.
-GEN('#if defined(OS_MACOSX)');
-GEN('#define MAYBE_NoItems DISABLED_NoItems');
-GEN('#else');
-GEN('#define MAYBE_NoItems NoItems');
-GEN('#endif');
-TEST_F('CrExtensionsItemListTest', 'MAYBE_NoItems', function() {
+TEST_F('CrExtensionsItemListTest', 'NoItems', function() {
   this.runMochaTest(extension_item_list_tests.TestNames.NoItemsMsg);
 });
 
diff --git a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
index 57beb41..5f34c28 100644
--- a/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
+++ b/chrome/test/data/webui/print_preview/invalid_settings_browsertest.js
@@ -29,6 +29,7 @@
       thousandsDelimiter: ',',
       decimalDelimiter: '.',
       unitType: 1,
+      previewIsPdf: false,
       previewModifiable: true,
       documentTitle: 'title',
       documentHasSelection: true,
@@ -75,7 +76,7 @@
 
       page = document.createElement('print-preview-app');
       document.body.appendChild(page);
-      page.$.documentInfo.init(true, 'title', false);
+      page.$.documentInfo.init(true, false, 'title', false);
       const previewArea = page.$.previewArea;
     }
 
@@ -289,7 +290,7 @@
           })
           .then(function() {
             // Set this to enable the scaling input.
-            page.setSetting('customScaling', true);
+            page.setSetting('scalingType', print_preview.ScalingType.CUSTOM);
 
             destinationSettings.destinationStore_.startLoadCloudDestinations();
 
diff --git a/chrome/test/data/webui/print_preview/model_settings_availability_test.js b/chrome/test/data/webui/print_preview/model_settings_availability_test.js
index d9ff578..1de7298a 100644
--- a/chrome/test/data/webui/print_preview/model_settings_availability_test.js
+++ b/chrome/test/data/webui/print_preview/model_settings_availability_test.js
@@ -341,33 +341,60 @@
 
     });
 
-    test('fit to page', function() {
+    test('scalingType', function() {
       // HTML -> printer
-      assertFalse(model.settings.fitToPage.available);
+      assertTrue(model.settings.scalingType.available);
 
       // HTML -> Save as PDF
       const defaultDestination = model.destination;
       setSaveAsPdfDestination();
-      assertFalse(model.settings.fitToPage.available);
+      assertTrue(model.settings.scalingType.available);
 
       // PDF -> Save as PDF
       model.set('documentSettings.isModifiable', false);
       model.set('documentSettings.isPdf', true);
-      assertFalse(model.settings.fitToPage.available);
+      assertFalse(model.settings.scalingType.available);
 
       // PDF -> printer
       model.set('destination', defaultDestination);
-      assertTrue(model.settings.fitToPage.available);
-      assertFalse(model.settings.fitToPage.setFromUi);
+      assertFalse(model.settings.scalingType.available);
 
       // Non-PDF Plugin -> Save as PDF
       setSaveAsPdfDestination();
       model.set('documentSettings.isPdf', false);
-      assertFalse(model.settings.fitToPage.available);
+      assertFalse(model.settings.scalingType.available);
 
       // Non-PDF Plugin -> printer
       model.set('destination', defaultDestination);
-      assertFalse(model.settings.fitToPage.available);
+      assertFalse(model.settings.scalingType.available);
+    });
+
+    test('scalingTypePdf', function() {
+      // HTML -> printer
+      assertFalse(model.settings.scalingTypePdf.available);
+
+      // HTML -> Save as PDF
+      const defaultDestination = model.destination;
+      setSaveAsPdfDestination();
+      assertFalse(model.settings.scalingTypePdf.available);
+
+      // PDF -> Save as PDF
+      model.set('documentSettings.isModifiable', false);
+      model.set('documentSettings.isPdf', true);
+      assertFalse(model.settings.scalingTypePdf.available);
+
+      // PDF -> printer
+      model.set('destination', defaultDestination);
+      assertTrue(model.settings.scalingTypePdf.available);
+
+      // Non-PDF Plugin -> Save as PDF
+      setSaveAsPdfDestination();
+      model.set('documentSettings.isPdf', false);
+      assertFalse(model.settings.scalingTypePdf.available);
+
+      // Non-PDF Plugin -> printer
+      model.set('destination', defaultDestination);
+      assertFalse(model.settings.scalingTypePdf.available);
     });
 
     test('header footer', function() {
diff --git a/chrome/test/data/webui/print_preview/model_test.js b/chrome/test/data/webui/print_preview/model_test.js
index fd5ebbf8..a0fec8b 100644
--- a/chrome/test/data/webui/print_preview/model_test.js
+++ b/chrome/test/data/webui/print_preview/model_test.js
@@ -35,11 +35,11 @@
         dpi: {},
         mediaSize: {},
         marginsType: 0, /* default */
-        customScaling: false,
         scaling: '100',
+        scalingType: print_preview.ScalingType.DEFAULT,
+        scalingTypePdf: print_preview.ScalingType.DEFAULT,
         isHeaderFooterEnabled: true,
         isCssBackgroundEnabled: false,
-        isFitToPageEnabled: false,
         isCollateEnabled: true,
         isDuplexEnabled: true,
         isDuplexShortEdge: false,
@@ -59,11 +59,11 @@
         dpi: {horizontal_dpi: 1000, vertical_dpi: 500},
         mediaSize: {width_microns: 43180, height_microns: 21590},
         marginsType: 2, /* none */
-        customScaling: true,
         scaling: '85',
+        scalingType: print_preview.ScalingType.CUSTOM,
+        scalingTypePdf: print_preview.ScalingType.FIT_TO_PAGE,
         isHeaderFooterEnabled: false,
         isCssBackgroundEnabled: true,
-        isFitToPageEnabled: true,
         isCollateEnabled: false,
         isDuplexEnabled: false,
         isDuplexShortEdge: true,
@@ -129,16 +129,15 @@
               .then(
                   () =>
                       testStickySetting('duplexShortEdge', 'isDuplexShortEdge'))
-              .then(() => testStickySetting('fitToPage', 'isFitToPageEnabled'))
               .then(
                   () => testStickySetting(
                       'headerFooter', 'isHeaderFooterEnabled'))
               .then(() => testStickySetting('layout', 'isLandscapeEnabled'))
               .then(() => testStickySetting('margins', 'marginsType'))
               .then(() => testStickySetting('mediaSize', 'mediaSize'))
-              .then(() => testStickySetting('customScaling', 'customScaling'))
               .then(() => testStickySetting('scaling', 'scaling'))
-              .then(() => testStickySetting('fitToPage', 'isFitToPageEnabled'))
+              .then(() => testStickySetting('scalingType', 'scalingType'))
+              .then(() => testStickySetting('scalingTypePdf', 'scalingTypePdf'))
               .then(() => testStickySetting('vendorItems', 'vendorOptions'));
       if (cr.isChromeOS) {
         promise = promise.then(() => testStickySetting('pin', 'isPinEnabled'))
@@ -178,9 +177,6 @@
 
     /** @param {!print_preview.Destination} testDestination */
     function toggleSettings(testDestination) {
-      // Some non default setting values to change to.
-      // Manually set fit to page to available so it can be toggled.
-      model.settings.fitToPage.available = true;
       const settingsChange = {
         pages: [2],
         copies: 2,
@@ -199,9 +195,9 @@
           horizontal_dpi: 100,
           vertical_dpi: 100,
         },
-        fitToPage: false,
-        customScaling: true,
         scaling: '90',
+        scalingType: print_preview.ScalingType.CUSTOM,
+        scalingTypePdf: print_preview.ScalingType.CUSTOM,
         duplex: true,
         duplexShortEdge: true,
         cssBackground: true,
@@ -230,6 +226,7 @@
         hasCssMediaStyles: false,
         hasSelection: true,
         isModifiable: true,
+        isPdf: false,
         isScalingDisabled: false,
         fitToPageScaling: 100,
         pageCount: 3,
@@ -285,11 +282,8 @@
         shouldPrintBackgrounds: false,
         shouldPrintSelectionOnly: false,
         previewModifiable: true,
-        printToPDF: false,
         printToGoogleDrive: false,
-        printWithCloudPrint: false,
-        printWithPrivet: false,
-        printWithExtension: false,
+        printerType: print_preview.PrinterType.LOCAL_PRINTER,
         rasterizePDF: false,
         scaleFactor: 100,
         pagesPerSheet: 1,
@@ -297,7 +291,7 @@
         dpiVertical: 200,
         dpiDefault: true,
         deviceName: 'FooDevice',
-        fitToPageEnabled: true,
+        fitToPageEnabled: false,
         pageWidth: 612,
         pageHeight: 792,
         showSystemDialog: false,
@@ -326,11 +320,8 @@
         shouldPrintBackgrounds: true,
         shouldPrintSelectionOnly: false,  // Only for Print Preview.
         previewModifiable: true,
-        printToPDF: false,
         printToGoogleDrive: false,
-        printWithCloudPrint: false,
-        printWithPrivet: false,
-        printWithExtension: false,
+        printerType: print_preview.PrinterType.LOCAL_PRINTER,
         rasterizePDF: true,
         scaleFactor: 90,
         pagesPerSheet: 1,
diff --git a/chrome/test/data/webui/print_preview/native_layer_stub.js b/chrome/test/data/webui/print_preview/native_layer_stub.js
index a755219..c630dc8 100644
--- a/chrome/test/data/webui/print_preview/native_layer_stub.js
+++ b/chrome/test/data/webui/print_preview/native_layer_stub.js
@@ -170,7 +170,8 @@
     /** @override */
     print(printTicket) {
       this.methodCalled('print', printTicket);
-      if (JSON.parse(printTicket).printWithCloudPrint) {
+      if (JSON.parse(printTicket).printerType ==
+          print_preview.PrinterType.CLOUD_PRINTER) {
         return Promise.resolve('sample data');
       }
       return Promise.resolve();
diff --git a/chrome/test/data/webui/print_preview/preview_generation_test.js b/chrome/test/data/webui/print_preview/preview_generation_test.js
index b34cee46..2f5df0d 100644
--- a/chrome/test/data/webui/print_preview/preview_generation_test.js
+++ b/chrome/test/data/webui/print_preview/preview_generation_test.js
@@ -128,13 +128,15 @@
           'cssBackground', false, true, 'shouldPrintBackgrounds', false, true);
     });
 
-    /** Validate changing the fit to page setting updates the preview. */
+    /** Validate changing the scalingTypePdf setting updates the preview. */
     test(assert(TestNames.FitToPage), function() {
       // Set PDF document so setting is available.
       initialSettings.previewModifiable = false;
       initialSettings.previewIsPdf = true;
       return testSimpleSetting(
-          'fitToPage', false, true, 'fitToPageEnabled', false, true);
+          'scalingTypePdf', print_preview.ScalingType.DEFAULT,
+          print_preview.ScalingType.FIT_TO_PAGE, 'fitToPageEnabled', false,
+          true);
     });
 
     /** Validate changing the header/footer setting updates the preview. */
@@ -353,8 +355,10 @@
             assertEquals(100, ticket.scaleFactor);
             nativeLayer.resetResolver('getPreview');
             assertEquals('100', page.getSettingValue('scaling'));
-            assertEquals(false, page.getSettingValue('customScaling'));
-            page.setSetting('customScaling', true);
+            assertEquals(
+                print_preview.ScalingType.DEFAULT,
+                page.getSettingValue('scalingType'));
+            page.setSetting('scalingType', print_preview.ScalingType.CUSTOM);
             // Need to set custom value != 100 for preview to regenerate.
             page.setSetting('scaling', '90');
             return nativeLayer.whenCalled('getPreview');
@@ -365,10 +369,12 @@
             assertEquals(1, ticket.requestID);
             nativeLayer.resetResolver('getPreview');
             assertEquals('90', page.getSettingValue('scaling'));
-            assertEquals(true, page.getSettingValue('customScaling'));
+            assertEquals(
+                print_preview.ScalingType.CUSTOM,
+                page.getSettingValue('scalingType'));
             // This should regenerate the preview, since the custom value is not
             // 100.
-            page.setSetting('customScaling', false);
+            page.setSetting('scalingType', print_preview.ScalingType.DEFAULT);
             return nativeLayer.whenCalled('getPreview');
           })
           .then(function(args) {
@@ -377,8 +383,10 @@
             assertEquals(2, ticket.requestID);
             nativeLayer.resetResolver('getPreview');
             assertEquals('90', page.getSettingValue('scaling'));
-            assertEquals(false, page.getSettingValue('customScaling'));
-            page.setSetting('customScaling', true);
+            assertEquals(
+                print_preview.ScalingType.DEFAULT,
+                page.getSettingValue('scalingType'));
+            page.setSetting('scalingType', print_preview.ScalingType.CUSTOM);
             return nativeLayer.whenCalled('getPreview');
           })
           .then(function(args) {
@@ -388,7 +396,9 @@
             assertEquals(3, ticket.requestID);
             nativeLayer.resetResolver('getPreview');
             assertEquals('90', page.getSettingValue('scaling'));
-            assertEquals(true, page.getSettingValue('customScaling'));
+            assertEquals(
+                print_preview.ScalingType.CUSTOM,
+                page.getSettingValue('scalingType'));
             // When custom scaling is set, changing scaling updates preview.
             page.setSetting('scaling', '80');
             return nativeLayer.whenCalled('getPreview');
@@ -398,7 +408,9 @@
             assertEquals(80, ticket.scaleFactor);
             assertEquals(4, ticket.requestID);
             assertEquals('80', page.getSettingValue('scaling'));
-            assertEquals(true, page.getSettingValue('customScaling'));
+            assertEquals(
+                print_preview.ScalingType.CUSTOM,
+                page.getSettingValue('scalingType'));
           });
     });
 
diff --git a/chrome/test/data/webui/print_preview/print_preview_test_utils.js b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
index b46056e8..c7813c2 100644
--- a/chrome/test/data/webui/print_preview/print_preview_test_utils.js
+++ b/chrome/test/data/webui/print_preview/print_preview_test_utils.js
@@ -11,6 +11,7 @@
       thousandsDelimiter: ',',
       decimalDelimiter: '.',
       unitType: 1,
+      previewIsPdf: false,
       previewModifiable: true,
       documentTitle: 'title',
       documentHasSelection: true,
diff --git a/chrome/test/data/webui/print_preview/restore_state_test.js b/chrome/test/data/webui/print_preview/restore_state_test.js
index 649383a..058468f 100644
--- a/chrome/test/data/webui/print_preview/restore_state_test.js
+++ b/chrome/test/data/webui/print_preview/restore_state_test.js
@@ -56,10 +56,10 @@
        ['headerFooter', 'isHeaderFooterEnabled'],
        ['layout', 'isLandscapeEnabled'],
        ['collate', 'isCollateEnabled'],
-       ['fitToPage', 'isFitToPageEnabled'],
        ['cssBackground', 'isCssBackgroundEnabled'],
        ['scaling', 'scaling'],
-       ['customScaling', 'customScaling'],
+       ['scalingType', 'scalingType'],
+       ['scalingTypePdf', 'scalingTypePdf'],
       ].forEach(keys => {
         assertEquals(stickySettings[keys[1]], page.settings[keys[0]].value);
       });
@@ -114,11 +114,11 @@
           printArea: 6,
         },
         marginsType: 3, /* custom */
-        customScaling: true,
         scaling: '90',
+        scalingType: print_preview.ScalingType.CUSTOM,
+        scalingTypePdf: print_preview.ScalingType.FIT_TO_PAGE,
         isHeaderFooterEnabled: true,
         isCssBackgroundEnabled: true,
-        isFitToPageEnabled: true,
         isCollateEnabled: true,
         isDuplexEnabled: true,
         isDuplexShortEdge: true,
@@ -154,11 +154,11 @@
           printArea: 4,
         },
         marginsType: 0, /* default */
-        customScaling: false,
         scaling: '120',
+        scalingType: print_preview.ScalingType.DEFAULT,
+        scalingTypePdf: print_preview.ScalingType.DEFAULT,
         isHeaderFooterEnabled: false,
         isCssBackgroundEnabled: false,
-        isFitToPageEnabled: false,
         isCollateEnabled: false,
         isDuplexEnabled: false,
         isDuplexShortEdge: false,
@@ -229,9 +229,15 @@
         },
         {
           section: 'print-preview-scaling-settings',
-          settingName: 'customScaling',
-          key: 'customScaling',
-          value: true,
+          settingName: 'scalingType',
+          key: 'scalingType',
+          value: print_preview.ScalingType.CUSTOM,
+        },
+        {
+          section: 'print-preview-scaling-settings',
+          settingName: 'scalingTypePdf',
+          key: 'scalingTypePdf',
+          value: print_preview.ScalingType.CUSTOM,
         },
         {
           section: 'print-preview-scaling-settings',
diff --git a/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.js b/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.js
index 0f49158..95c464c 100644
--- a/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.js
+++ b/chrome/test/data/webui/print_preview/scaling_settings_interactive_test.js
@@ -21,11 +21,12 @@
       PolymerTest.clearBody();
       model = document.createElement('print-preview-model');
       document.body.appendChild(model);
-      model.set('settings.fitToPage.available', false);
+      model.set('settings.scalingTypePdf.available', false);
 
       scalingSection = document.createElement('print-preview-scaling-settings');
       scalingSection.settings = model.settings;
       scalingSection.disabled = false;
+      scalingSection.isPdf = false;
       test_util.fakeDataBind(model, scalingSection, 'settings');
       document.body.appendChild(scalingSection);
     });
@@ -38,7 +39,9 @@
       const collapse = scalingSection.$$('iron-collapse');
 
       assertFalse(collapse.opened);
-      assertFalse(scalingSection.getSettingValue('customScaling'));
+      assertEquals(
+          print_preview.ScalingType.DEFAULT,
+          scalingSection.getSettingValue('scalingType'));
 
       // Select custom with the dropdown. This should autofocus the input.
       await Promise.all([
@@ -56,12 +59,15 @@
             scalingSection, scalingSection.ScalingValue.DEFAULT.toString()),
         test_util.eventToPromise('transitionend', collapse),
       ]);
-      assertFalse(scalingSection.getSettingValue('customScaling'));
+      assertEquals(
+          print_preview.ScalingType.DEFAULT,
+          scalingSection.getSettingValue('scalingType'));
       assertFalse(scalingSection.$$('iron-collapse').opened);
 
       // Set custom in JS, which happens when we set the sticky settings. This
       // should not autofocus the input.
-      scalingSection.setSetting('customScaling', true);
+      scalingSection.setSetting(
+          'scalingType', print_preview.ScalingType.CUSTOM);
       await test_util.eventToPromise('transitionend', collapse);
       assertTrue(collapse.opened);
       assertNotEquals(scalingInput, getDeepActiveElement());
diff --git a/chrome/test/data/webui/print_preview/scaling_settings_test.js b/chrome/test/data/webui/print_preview/scaling_settings_test.js
index e7b21e07..42cdaa8 100644
--- a/chrome/test/data/webui/print_preview/scaling_settings_test.js
+++ b/chrome/test/data/webui/print_preview/scaling_settings_test.js
@@ -23,11 +23,11 @@
       PolymerTest.clearBody();
       model = document.createElement('print-preview-model');
       document.body.appendChild(model);
-      model.set('settings.fitToPage.available', false);
 
       scalingSection = document.createElement('print-preview-scaling-settings');
       scalingSection.settings = model.settings;
       scalingSection.disabled = false;
+      setDocumentPdf(false);
       test_util.fakeDataBind(model, scalingSection, 'settings');
       document.body.appendChild(scalingSection);
     });
@@ -45,7 +45,7 @@
       assertFalse(customOption.hidden);
 
       // Fit to page available -> All 3 options.
-      model.set('settings.fitToPage.available', true);
+      setDocumentPdf(true);
       assertFalse(fitToPageOption.hidden);
       assertFalse(defaultOption.hidden);
       assertFalse(customOption.hidden);
@@ -54,26 +54,44 @@
     /**
      * @param {string} expectedScaling The expected scaling value.
      * @param {boolean} valid Whether the scaling setting is valid.
-     * @param {boolean} isCustom Whether custom scaling should be set.
-     * @param {boolean} fitToPage Expected fit to page value.
+     * @param {print_preview.ScalingType} scalingType Expected scaling type for
+     *     modifiable content.
+     * @param {print_preview.ScalingType} scalingTypePdf Expected scaling type
+     *     for PDFs.
      * @param {string} scalingDisplayValue The value that should be displayed in
      *     the UI for scaling.
      */
     function validateState(
-        expectedScaling, valid, isCustom, fitToPage, scalingDisplayValue) {
+        expectedScaling, valid, scalingType, scalingTypePdf,
+        scalingDisplayValue) {
       // Validate the settings were set as expected.
       assertEquals(expectedScaling, scalingSection.getSettingValue('scaling'));
       assertEquals(valid, scalingSection.getSetting('scaling').valid);
-      assertEquals(fitToPage, scalingSection.getSettingValue('fitToPage'));
-      assertEquals(isCustom, scalingSection.getSettingValue('customScaling'));
+      assertEquals(scalingType, scalingSection.getSetting('scalingType').value);
+      assertEquals(
+          scalingTypePdf, scalingSection.getSetting('scalingTypePdf').value);
 
       // Validate UI values that are set by JS.
       const scalingInput =
           scalingSection.$$('print-preview-number-settings-section').getInput();
+      const expectedCollapseOpened =
+          (scalingSection.getSettingValue('scalingType') ===
+           print_preview.ScalingType.CUSTOM) ||
+          (scalingSection.getSettingValue('scalingTypePdf') ===
+           print_preview.ScalingType.CUSTOM);
       const collapse = scalingSection.$$('iron-collapse');
       assertEquals(!valid, scalingInput.invalid);
       assertEquals(scalingDisplayValue, scalingInput.value);
-      assertEquals(isCustom && !fitToPage, collapse.opened);
+      assertEquals(expectedCollapseOpened, collapse.opened);
+    }
+
+    /**
+     * @param {boolean} isPdf Whether the document is a PDF
+     */
+    function setDocumentPdf(isPdf) {
+      model.set('settings.scalingType.available', !isPdf);
+      model.set('settings.scalingTypePdf.available', isPdf);
+      scalingSection.isPdf = isPdf;
     }
 
     // Verifies that setting the scaling value using the dropdown and/or the
@@ -86,74 +104,98 @@
       const scalingDropdown = scalingSection.$$('.md-select');
 
       // Make fit to page available.
-      model.set('settings.fitToPage.available', true);
+      setDocumentPdf(true);
 
       // Default is 100
-      validateState('100', true, false, false, '100');
+      validateState(
+          '100', true, print_preview.ScalingType.DEFAULT,
+          print_preview.ScalingType.DEFAULT, '100');
       assertFalse(scalingSection.getSetting('scaling').setFromUi);
-      assertFalse(scalingSection.getSetting('customScaling').setFromUi);
-      assertFalse(scalingSection.getSetting('fitToPage').setFromUi);
+      assertFalse(scalingSection.getSetting('scalingType').setFromUi);
+      assertFalse(scalingSection.getSetting('scalingTypePdf').setFromUi);
 
       // Select custom
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.CUSTOM.toString());
-      validateState('100', true, true, false, '100');
-      assertTrue(scalingSection.getSetting('customScaling').setFromUi);
-      assertTrue(scalingSection.getSetting('fitToPage').setFromUi);
+      validateState(
+          '100', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '100');
+      assertTrue(scalingSection.getSetting('scalingType').setFromUi);
+      assertTrue(scalingSection.getSetting('scalingTypePdf').setFromUi);
 
       await print_preview_test_utils.triggerInputEvent(
           scalingInput, '105', scalingSection);
-      validateState('105', true, true, false, '105');
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '105');
       assertTrue(scalingSection.getSetting('scaling').setFromUi);
 
       // Change to fit to page.
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.FIT_TO_PAGE.toString());
-      validateState('105', true, true, true, '105');
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.FIT_TO_PAGE, '105');
 
       // Go back to custom. Restores 105 value.
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.CUSTOM.toString());
-      validateState('105', true, true, false, '105');
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '105');
 
       // Set scaling to something invalid. Should change setting validity
       // but not value.
       await print_preview_test_utils.triggerInputEvent(
           scalingInput, '5', scalingSection);
-      validateState('105', false, true, false, '5');
+      validateState(
+          '105', false, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '5');
 
       // Select fit to page. Should clear the invalid value.
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.FIT_TO_PAGE.toString());
-      validateState('105', true, true, true, '105');
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.FIT_TO_PAGE, '105');
 
       // Custom scaling should set to last valid.
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.CUSTOM.toString());
-      validateState('105', true, true, false, '105');
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '105');
 
       // Set scaling to something invalid. Should change setting validity
       // but not value.
       await print_preview_test_utils.triggerInputEvent(
           scalingInput, '500', scalingSection);
-      validateState('105', false, true, false, '500');
+      validateState(
+          '105', false, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '500');
 
       // Pick default scaling. This should clear the error.
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.DEFAULT.toString());
-      validateState('105', true, false, false, '105');
+      validateState(
+          '105', true, print_preview.ScalingType.DEFAULT,
+          print_preview.ScalingType.DEFAULT, '105');
 
       // Custom scaling should set to last valid.
       await print_preview_test_utils.selectOption(
           scalingSection, scalingSection.ScalingValue.CUSTOM.toString());
-      validateState('105', true, true, false, '105');
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '105');
 
       // Enter a blank value in the scaling field. This should not
       // change the stored value of scaling or fit to page, to avoid an
       // unnecessary preview regeneration.
       await print_preview_test_utils.triggerInputEvent(
           scalingInput, '', scalingSection);
-      validateState('105', true, true, false, '');
+      validateState(
+          '105', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '');
     });
 
 
@@ -174,35 +216,47 @@
       });
 
       await print_preview_test_utils.selectOption(
-          scalingSection, scalingSection.ScalingValue.CUSTOM.toString());
+          scalingSection, scalingSection.ScalingValue.CUSTOM);
       await print_preview_test_utils.triggerInputEvent(
           input, '90', scalingSection);
-      validateState('90', true, true, false, '90');
+      validateState(
+          '90', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '90');
 
       // Set invalid input
       await print_preview_test_utils.triggerInputEvent(
           input, '9', scalingSection);
-      validateState('90', false, true, false, '9');
+      validateState(
+          '90', false, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '9');
 
       // Restore valid input
       await print_preview_test_utils.triggerInputEvent(
           input, '90', scalingSection);
-      validateState('90', true, true, false, '90');
+      validateState(
+          '90', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '90');
 
       // Invalid input again
       await print_preview_test_utils.triggerInputEvent(
           input, '9', scalingSection);
-      validateState('90', false, true, false, '9');
+      validateState(
+          '90', false, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '9');
 
       // Clear input
       await print_preview_test_utils.triggerInputEvent(
           input, '', scalingSection);
-      validateState('90', true, true, false, '');
+      validateState(
+          '90', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '');
 
       // Set valid input
       await print_preview_test_utils.triggerInputEvent(
           input, '50', scalingSection);
-      validateState('50', true, true, false, '50');
+      validateState(
+          '50', true, print_preview.ScalingType.CUSTOM,
+          print_preview.ScalingType.CUSTOM, '50');
     });
   });
 
diff --git a/chrome/test/data/webui/tab_strip/tab_test.js b/chrome/test/data/webui/tab_strip/tab_test.js
index 6131e3c..47dfbe9 100644
--- a/chrome/test/data/webui/tab_strip/tab_test.js
+++ b/chrome/test/data/webui/tab_strip/tab_test.js
@@ -5,7 +5,8 @@
 import 'chrome://tab-strip/tab.js';
 
 import {getFavicon, getFaviconForPageURL} from 'chrome://resources/js/icon.m.js';
-import {TabsApiProxy} from 'chrome://tab-strip/tabs_api_proxy.js';
+import {TabNetworkState, TabsApiProxy} from 'chrome://tab-strip/tabs_api_proxy.js';
+
 import {TestTabsApiProxy} from './test_tabs_api_proxy.js';
 
 suite('Tab', function() {
@@ -14,7 +15,7 @@
 
   const tab = {
     id: 1001,
-    status: 'complete',
+    networkState: TabNetworkState.NONE,
     title: 'My title',
   };
 
@@ -65,12 +66,23 @@
   });
 
   test('toggles a [loading] attribute when loading', () => {
-    tabElement.tab = Object.assign({}, tab, {status: 'loading'});
+    tabElement.tab =
+        Object.assign({}, tab, {networkState: TabNetworkState.LOADING});
     assertTrue(tabElement.hasAttribute('loading'));
-    tabElement.tab = Object.assign({}, tab, {status: 'complete'});
+    tabElement.tab =
+        Object.assign({}, tab, {networkState: TabNetworkState.NONE});
     assertFalse(tabElement.hasAttribute('loading'));
   });
 
+  test('toggles a [waiting] attribute when waiting', () => {
+    tabElement.tab =
+        Object.assign({}, tab, {networkState: TabNetworkState.WAITING});
+    assertTrue(tabElement.hasAttribute('waiting'));
+    tabElement.tab =
+        Object.assign({}, tab, {networkState: TabNetworkState.NONE});
+    assertFalse(tabElement.hasAttribute('waiting'));
+  });
+
   test('clicking on the element activates the tab', () => {
     tabElement.click();
     return testTabsApiProxy.whenCalled('activateTab', tabId => {
@@ -116,7 +128,8 @@
       'removes the favicon if the tab is loading and there is no favicon URL',
       () => {
         delete tab.favIconUrl;
-        tabElement.tab = Object.assign({}, tab, {status: 'loading'});
+        tabElement.tab =
+            Object.assign({}, tab, {networkState: TabNetworkState.LOADING});
         const faviconElement = tabElement.shadowRoot.querySelector('#favicon');
         assertEquals(faviconElement.style.backgroundImage, 'none');
       });
@@ -151,4 +164,17 @@
         tabElement.getDragImage(),
         tabElement.shadowRoot.querySelector('#dragImage'));
   });
+
+  test('has custom context menu', async () => {
+    let event = new Event('contextmenu');
+    event.clientX = 1;
+    event.clientY = 2;
+    tabElement.dispatchEvent(event);
+
+    const contextMenuArgs =
+        await testTabsApiProxy.whenCalled('showTabContextMenu');
+    assertEquals(contextMenuArgs[0], tabElement.tab.id);
+    assertEquals(contextMenuArgs[1], 1);
+    assertEquals(contextMenuArgs[2], 2);
+  });
 });
diff --git a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
index 4dfeafe..55a573c 100644
--- a/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
+++ b/chrome/test/data/webui/tab_strip/test_tabs_api_proxy.js
@@ -12,6 +12,7 @@
       'getTabs',
       'moveTab',
       'trackThumbnailForTab',
+      'showTabContextMenu',
     ]);
 
     this.tabs_;
@@ -41,6 +42,10 @@
     this.tabs_ = tabs;
   }
 
+  showTabContextMenu(tabId, locationX, locationY) {
+    this.methodCalled('showTabContextMenu', [tabId, locationX, locationY]);
+  }
+
   trackThumbnailForTab(tabId) {
     this.methodCalled('trackThumbnailForTab', tabId);
   }
diff --git a/chromecast/browser/webview/webview_controller.cc b/chromecast/browser/webview/webview_controller.cc
index f790dcf..b5855cc 100644
--- a/chromecast/browser/webview/webview_controller.cc
+++ b/chromecast/browser/webview/webview_controller.cc
@@ -15,6 +15,7 @@
 #include "content/public/browser/browsing_data_remover.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/browser/render_view_host.h"
+#include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/web_preferences.h"
 #include "ui/aura/window.h"
@@ -55,6 +56,7 @@
   cast_contents_init.delegate = this;
   cast_web_contents_ = std::make_unique<CastWebContentsImpl>(
       contents_.get(), cast_contents_init);
+  cast_web_contents_->AddObserver(this);
 
   std::unique_ptr<webview::WebviewResponse> response =
       std::make_unique<webview::WebviewResponse>();
@@ -65,7 +67,9 @@
   client->EnqueueSend(std::move(response));
 }
 
-WebviewController::~WebviewController() {}
+WebviewController::~WebviewController() {
+  cast_web_contents_->RemoveObserver(this);
+}
 
 std::unique_ptr<content::NavigationThrottle>
 WebviewController::MaybeGetNavigationThrottle(
@@ -222,7 +226,8 @@
   if (!contents_->GetNativeView()->HasFocus())
     contents_->GetNativeView()->Focus();
 
-  ui::EventHandler* handler = contents_->GetNativeView()->delegate();
+  ui::EventHandler* handler =
+      contents_->GetRenderWidgetHostView()->GetNativeView()->delegate();
   ui::EventType type = static_cast<ui::EventType>(ev.event_type());
   switch (type) {
     case ui::ET_TOUCH_RELEASED:
@@ -243,7 +248,33 @@
                 touch.twist(), touch.tilt_x(), touch.tilt_y(),
                 touch.tangential_pressure()),
             ev.flags());
+
+        ui::TouchEvent root_relative_event(evt);
+        root_relative_event.set_location_f(evt.root_location_f());
+
+        // GestureRecognizerImpl makes several APIs private so cast it to the
+        // interface.
+        ui::GestureRecognizer* recognizer = &gesture_recognizer_;
+
+        // Run touches through the gesture recognition pipeline, web content
+        // typically wants to process gesture events, not touch events.
+        if (!recognizer->ProcessTouchEventPreDispatch(
+                &root_relative_event, contents_->GetNativeView())) {
+          return;
+        }
+
         handler->OnTouchEvent(&evt);
+
+        // Normally this would be done when the renderer acknowledges the touch
+        // event and using flags from the renderer, inside
+        // RenderWidgetHostViewAura, but we don't have those so... fake it.
+        auto list =
+            recognizer->AckTouchEvent(evt.unique_event_id(), ui::ER_UNHANDLED,
+                                      false, contents_->GetNativeView());
+        for (auto& e : list) {
+          // Forward all gestures.
+          handler->OnGestureEvent(e.get());
+        }
       } else {
         client_->OnError("touch() not supplied for touch event");
       }
diff --git a/chromecast/browser/webview/webview_controller.h b/chromecast/browser/webview/webview_controller.h
index 107da04..7efbbfd2 100644
--- a/chromecast/browser/webview/webview_controller.h
+++ b/chromecast/browser/webview/webview_controller.h
@@ -11,6 +11,7 @@
 #include "base/supports_user_data.h"
 #include "chromecast/browser/cast_web_contents.h"
 #include "chromecast/browser/webview/proto/webview.pb.h"
+#include "ui/events/gestures/gesture_recognizer_impl.h"
 #include "url/gurl.h"
 
 namespace aura {
@@ -107,6 +108,8 @@
 
   bool has_navigation_delegate_ = false;
 
+  ui::GestureRecognizerImpl gesture_recognizer_;
+
   // The navigation throttle for the current navigation event, if any.
   // Is set only:
   //    When has_navigation_delegate is true, and
diff --git a/chromeos/services/assistant/platform/audio_input_impl.cc b/chromeos/services/assistant/platform/audio_input_impl.cc
index 16832d7..d9a5dea29 100644
--- a/chromeos/services/assistant/platform/audio_input_impl.cc
+++ b/chromeos/services/assistant/platform/audio_input_impl.cc
@@ -193,15 +193,13 @@
 AudioInputImpl::AudioInputImpl(mojom::Client* client,
                                PowerManagerClient* power_manager_client,
                                CrasAudioHandler* cras_audio_handler,
-                               const std::string& device_id,
-                               const std::string& hotword_device_id)
+                               const std::string& device_id)
     : client_(client),
       power_manager_client_(power_manager_client),
       power_manager_client_observer_(this),
       cras_audio_handler_(cras_audio_handler),
       task_runner_(base::SequencedTaskRunnerHandle::Get()),
-      device_id_(device_id),
-      hotword_device_id_(hotword_device_id),
+      preferred_device_id_(device_id),
       weak_factory_(this) {
   DETACH_FROM_SEQUENCE(observer_sequence_checker_);
 
@@ -287,15 +285,7 @@
 void AudioInputImpl::AddObserver(
     assistant_client::AudioInput::Observer* observer) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(observer_sequence_checker_);
-  VLOG(1) << device_id_ << " add observer";
-
-  // Feed the observer one frame of empty data to work around crbug/942268
-  std::vector<int16_t> buffer(g_current_format.num_channels);
-  int64_t time = features::IsAudioEraserEnabled()
-                     ? base::TimeTicks::Now().since_origin().InMicroseconds()
-                     : 0;
-  AudioInputBufferImpl input_buffer(buffer.data(), /*frame_count=*/1);
-  observer->OnAudioBufferAvailable(input_buffer, time);
+  VLOG(1) << " add observer";
 
   bool have_first_observer = false;
   {
@@ -372,18 +362,20 @@
 void AudioInputImpl::OnHotwordEnabled(bool enable) {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
-  if (default_on_ == enable)
+  if (hotword_enabled_ == enable)
     return;
 
-  default_on_ = enable;
+  hotword_enabled_ = enable;
   UpdateRecordingState();
 }
 
 void AudioInputImpl::SetDeviceId(const std::string& device_id) {
-  if (device_id_ == device_id)
+  if (preferred_device_id_ == device_id)
     return;
 
-  device_id_ = device_id;
+  preferred_device_id_ = device_id;
+
+  UpdateRecordingState();
   if (source_)
     state_manager_->RecreateAudioInputStream();
 }
@@ -449,29 +441,32 @@
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   StopRecording();
 
+  device_id_ = preferred_device_id_.empty()
+                   ? media::AudioDeviceDescription::kDefaultDeviceId
+                   : preferred_device_id_;
+
   // AUDIO_PCM_LINEAR and AUDIO_PCM_LOW_LATENCY are the same on CRAS.
   auto param = media::AudioParameters(
       media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
       GetChannelLayout(g_current_format), g_current_format.sample_rate,
       g_current_format.sample_rate / 10 /* buffer size for 100 ms */);
 
-  std::string* device_id = &device_id_;
   if (use_dsp && !hotword_device_id_.empty()) {
     param.set_effects(media::AudioParameters::PlatformEffectsMask::HOTWORD);
-    device_id = &hotword_device_id_;
+    device_id_ = hotword_device_id_;
   }
 
   mojo::PendingRemote<audio::mojom::StreamFactory> stream_factory;
   client_->RequestAudioStreamFactory(
       stream_factory.InitWithNewPipeAndPassReceiver());
-  source_ = audio::CreateInputDevice(std::move(stream_factory), *device_id);
+  source_ = audio::CreateInputDevice(std::move(stream_factory), device_id_);
 
   source_->Initialize(param, this);
   source_->Start();
   VLOG(1) << device_id_ << " start recording";
 }
 
-bool AudioInputImpl::IsHotwordAvailable() {
+bool AudioInputImpl::IsHotwordAvailable() const {
   return features::IsDspHotwordEnabled() && !hotword_device_id_.empty();
 }
 
@@ -479,6 +474,10 @@
   return !!source_;
 }
 
+bool AudioInputImpl::IsUsingHotwordDeviceForTesting() const {
+  return device_id_ == hotword_device_id_ && IsHotwordAvailable();
+}
+
 void AudioInputImpl::StartRecording() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
   DCHECK(!source_);
@@ -491,6 +490,7 @@
     VLOG(1) << device_id_ << " stop recording";
     source_->Stop();
     source_.reset();
+    device_id_ = std::string();
     VLOG(1) << device_id_
             << " ending captured frames: " << captured_frames_count_;
   }
@@ -508,21 +508,19 @@
 void AudioInputImpl::UpdateRecordingState() {
   DCHECK(task_runner_->RunsTasksInCurrentSequence());
 
-  bool should_start;
+  bool has_observers = false;
   {
     base::AutoLock lock(lock_);
-
-    switch (lid_state_) {
-      case chromeos::PowerManagerClient::LidState::OPEN:
-      case chromeos::PowerManagerClient::LidState::NOT_PRESENT:
-        should_start = (default_on_ || mic_open_) && observers_.size() > 0;
-        break;
-      case chromeos::PowerManagerClient::LidState::CLOSED:
-        should_start = false;
-        break;
-    }
+    has_observers = observers_.size() > 0;
   }
 
+  bool is_lid_closed =
+      lid_state_ == chromeos::PowerManagerClient::LidState::CLOSED;
+  bool should_enable_hotword =
+      hotword_enabled_ && (!preferred_device_id_.empty());
+  bool should_start =
+      !is_lid_closed && (should_enable_hotword || mic_open_) && has_observers;
+
   if (!source_ && should_start)
     StartRecording();
   else if (source_ && !should_start)
diff --git a/chromeos/services/assistant/platform/audio_input_impl.h b/chromeos/services/assistant/platform/audio_input_impl.h
index 0e3f6f6..cc0acd1 100644
--- a/chromeos/services/assistant/platform/audio_input_impl.h
+++ b/chromeos/services/assistant/platform/audio_input_impl.h
@@ -35,8 +35,7 @@
   AudioInputImpl(mojom::Client* client,
                  PowerManagerClient* power_manager_client,
                  CrasAudioHandler* cras_audio_handler,
-                 const std::string& device_id,
-                 const std::string& hotword_device_id);
+                 const std::string& device_id);
   ~AudioInputImpl() override;
 
   class HotwordStateManager {
@@ -92,10 +91,12 @@
 
   void RecreateAudioInputStream(bool use_dsp);
 
-  bool IsHotwordAvailable();
+  bool IsHotwordAvailable() const;
 
   // Returns the recording state used in unittests.
   bool IsRecordingForTesting() const;
+  // Returns if the hotword device is used for recording now.
+  bool IsUsingHotwordDeviceForTesting() const;
 
  private:
   void StartRecording();
@@ -108,12 +109,12 @@
 
   scoped_refptr<media::AudioCapturerSource> source_;
 
-  // Should audio input always recording actively.
-  bool default_on_ = true;
-
   // User explicitly requested to open microphone.
   bool mic_open_ = false;
 
+  // Whether hotword is currently enabled.
+  bool hotword_enabled_ = true;
+
   // Guards observers_;
   base::Lock lock_;
   std::vector<assistant_client::AudioInput::Observer*> observers_;
@@ -142,10 +143,12 @@
 
   std::unique_ptr<HotwordStateManager> state_manager_;
 
-  // Audio input device which will be used for capture.
-  std::string device_id_;
+  // Preferred audio input device which will be used for capture.
+  std::string preferred_device_id_;
   // Hotword input device used for hardware based hotword detection.
   std::string hotword_device_id_;
+  // Device currently being used for recording.
+  std::string device_id_;
 
   chromeos::PowerManagerClient::LidState lid_state_ =
       chromeos::PowerManagerClient::LidState::NOT_PRESENT;
diff --git a/chromeos/services/assistant/platform/audio_input_impl_unittest.cc b/chromeos/services/assistant/platform/audio_input_impl_unittest.cc
index 43f2b39..479c5f1 100644
--- a/chromeos/services/assistant/platform/audio_input_impl_unittest.cc
+++ b/chromeos/services/assistant/platform/audio_input_impl_unittest.cc
@@ -54,7 +54,7 @@
 
     audio_input_impl_ = std::make_unique<AudioInputImpl>(
         &fake_assistant_client_, FakePowerManagerClient::Get(),
-        CrasAudioHandler::Get(), "fake-device-id", "fake-hotword-device-id");
+        CrasAudioHandler::Get(), "fake-device-id");
 
     audio_input_impl_->AddObserver(this);
   }
@@ -62,6 +62,7 @@
   ~AudioInputImplTest() override {
     audio_input_impl_->RemoveObserver(this);
     audio_input_impl_.reset();
+    CrasAudioHandler::Shutdown();
     chromeos::PowerManagerClient::Shutdown();
   }
 
@@ -69,6 +70,12 @@
     return audio_input_impl_->IsRecordingForTesting();
   }
 
+  bool IsUsingHotwordDevice() const {
+    return audio_input_impl_->IsUsingHotwordDeviceForTesting();
+  }
+
+  AudioInputImpl* audio_input_impl() { return audio_input_impl_.get(); }
+
   // assistant_client::AudioInput::Observer overrides:
   void OnAudioBufferAvailable(const assistant_client::AudioBuffer& buffer,
                               int64_t timestamp) override {}
@@ -104,5 +111,79 @@
   EXPECT_TRUE(GetRecordingStatus());
 }
 
+TEST_F(AudioInputImplTest, StopRecordingWithNoPreferredDevice) {
+  // Start as recording.
+  ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
+  EXPECT_TRUE(GetRecordingStatus());
+
+  // Preferred input device is lost.
+  audio_input_impl()->SetDeviceId(std::string());
+  EXPECT_FALSE(GetRecordingStatus());
+
+  // Preferred input device is set again.
+  audio_input_impl()->SetDeviceId("fake-device_id");
+  EXPECT_TRUE(GetRecordingStatus());
+}
+
+TEST_F(AudioInputImplTest, StopRecordingWhenDisableHotword) {
+  // Start as recording.
+  ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
+  EXPECT_TRUE(GetRecordingStatus());
+
+  // Hotword disabled should stop recording.
+  audio_input_impl()->OnHotwordEnabled(false);
+  EXPECT_FALSE(GetRecordingStatus());
+
+  // Hotword enabled again should start recording.
+  audio_input_impl()->OnHotwordEnabled(true);
+  EXPECT_TRUE(GetRecordingStatus());
+}
+
+TEST_F(AudioInputImplTest, StartRecordingWhenDisableHotwordAndForceOpenMic) {
+  // Start as recording.
+  ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
+  EXPECT_TRUE(GetRecordingStatus());
+
+  // Hotword disabled should stop recording.
+  audio_input_impl()->OnHotwordEnabled(false);
+  EXPECT_FALSE(GetRecordingStatus());
+
+  // Force open mic should start recording.
+  audio_input_impl()->SetMicState(true);
+  EXPECT_TRUE(GetRecordingStatus());
+
+  // Stop force open mic should stop recording.
+  audio_input_impl()->SetMicState(false);
+  EXPECT_FALSE(GetRecordingStatus());
+}
+
+TEST_F(AudioInputImplTest, SettingHotwordDeviceDoesNotAffectRecordingState) {
+  // Start as recording.
+  ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
+  EXPECT_TRUE(GetRecordingStatus());
+
+  // Hotword device does not change recording state.
+  audio_input_impl()->SetHotwordDeviceId(std::string());
+  EXPECT_TRUE(GetRecordingStatus());
+
+  audio_input_impl()->SetHotwordDeviceId("fake-hotword-device");
+  EXPECT_TRUE(GetRecordingStatus());
+}
+
+TEST_F(AudioInputImplTest, SettingHotwordDeviceUsesHotwordDeviceForRecording) {
+  // Start as recording.
+  ReportLidEvent(chromeos::PowerManagerClient::LidState::OPEN);
+  EXPECT_TRUE(GetRecordingStatus());
+
+  // Hotword device does not change recording state.
+  audio_input_impl()->SetHotwordDeviceId(std::string());
+  EXPECT_TRUE(GetRecordingStatus());
+  EXPECT_FALSE(IsUsingHotwordDevice());
+
+  audio_input_impl()->SetHotwordDeviceId("fake-hotword-device");
+  EXPECT_TRUE(GetRecordingStatus());
+  EXPECT_TRUE(IsUsingHotwordDevice());
+}
+
 }  // namespace assistant
 }  // namespace chromeos
diff --git a/chromeos/services/assistant/platform/audio_input_provider_impl.cc b/chromeos/services/assistant/platform/audio_input_provider_impl.cc
index 9b60e36..863ba6717 100644
--- a/chromeos/services/assistant/platform/audio_input_provider_impl.cc
+++ b/chromeos/services/assistant/platform/audio_input_provider_impl.cc
@@ -12,14 +12,11 @@
 AudioInputProviderImpl::AudioInputProviderImpl(
     mojom::Client* client,
     PowerManagerClient* power_manager_client,
-    CrasAudioHandler* cras_audio_handler,
-    const std::string& input_device_id,
-    const std::string& hotword_device_id)
+    CrasAudioHandler* cras_audio_handler)
     : audio_input_(client,
                    power_manager_client,
                    cras_audio_handler,
-                   input_device_id,
-                   hotword_device_id) {}
+                   /*input_device_id=*/std::string()) {}
 
 AudioInputProviderImpl::~AudioInputProviderImpl() = default;
 
diff --git a/chromeos/services/assistant/platform/audio_input_provider_impl.h b/chromeos/services/assistant/platform/audio_input_provider_impl.h
index cf7d2d9..2b0647ca 100644
--- a/chromeos/services/assistant/platform/audio_input_provider_impl.h
+++ b/chromeos/services/assistant/platform/audio_input_provider_impl.h
@@ -24,9 +24,7 @@
  public:
   AudioInputProviderImpl(mojom::Client* client,
                          PowerManagerClient* power_manager_client,
-                         CrasAudioHandler* cras_audio_handler,
-                         const std::string& input_device_id,
-                         const std::string& hotword_device_id);
+                         CrasAudioHandler* cras_audio_handler);
   ~AudioInputProviderImpl() override;
 
   // assistant_client::AudioInputProvider overrides:
diff --git a/chromeos/services/assistant/platform/audio_output_provider_impl.cc b/chromeos/services/assistant/platform/audio_output_provider_impl.cc
index 2c57d72..4b23aba 100644
--- a/chromeos/services/assistant/platform/audio_output_provider_impl.cc
+++ b/chromeos/services/assistant/platform/audio_output_provider_impl.cc
@@ -150,8 +150,7 @@
       loop_back_input_(client,
                        power_manager_client,
                        cras_audio_handler,
-                       media::AudioDeviceDescription::kLoopbackInputDeviceId,
-                       /*hotword_device_id=*/std::string()),
+                       media::AudioDeviceDescription::kLoopbackInputDeviceId),
       volume_control_impl_(client, media_session),
       main_task_runner_(base::SequencedTaskRunnerHandle::Get()),
       background_task_runner_(background_task_runner),
diff --git a/chromeos/services/assistant/platform_api_impl.cc b/chromeos/services/assistant/platform_api_impl.cc
index 90143a85..37ea7be 100644
--- a/chromeos/services/assistant/platform_api_impl.cc
+++ b/chromeos/services/assistant/platform_api_impl.cc
@@ -86,11 +86,7 @@
     scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
     scoped_refptr<base::SingleThreadTaskRunner> background_task_runner,
     std::string pref_locale)
-    : audio_input_provider_(client,
-                            power_manager_client,
-                            cras_audio_handler,
-                            media::AudioDeviceDescription::kDefaultDeviceId,
-                            /*hotword_device_id=*/std::string()),
+    : audio_input_provider_(client, power_manager_client, cras_audio_handler),
       audio_output_provider_(client,
                              power_manager_client,
                              cras_audio_handler,
@@ -171,12 +167,16 @@
         break;
     }
   }
-  if (input_device)
-    audio_input_provider_.SetDeviceId(base::NumberToString(input_device->id));
+
+  audio_input_provider_.SetDeviceId(
+      input_device ? base::NumberToString(input_device->id) : std::string());
+
   if (hotword_device) {
     audio_input_provider_.SetHotwordDeviceId(
         base::NumberToString(hotword_device->id));
     audio_input_provider_.SetDspHotwordLocale(pref_locale_);
+  } else {
+    audio_input_provider_.SetHotwordDeviceId(std::string());
   }
 }
 
diff --git a/chromeos/services/network_config/cros_network_config.cc b/chromeos/services/network_config/cros_network_config.cc
index a57c4f9..9e3dfbe 100644
--- a/chromeos/services/network_config/cros_network_config.cc
+++ b/chromeos/services/network_config/cros_network_config.cc
@@ -503,6 +503,14 @@
   dict->SetStringKey(key, *property);
 }
 
+void SetStringIfNotEmpty(const char* key,
+                         const base::Optional<std::string>& property,
+                         base::Value* dict) {
+  if (!property || property->empty())
+    return;
+  dict->SetStringKey(key, *property);
+}
+
 void SetStringList(const char* key,
                    const base::Optional<std::vector<std::string>>& property,
                    base::Value* dict) {
@@ -1446,35 +1454,19 @@
 
 std::unique_ptr<base::DictionaryValue> GetOncFromConfigProperties(
     const mojom::ConfigProperties* properties) {
-  std::string onc_type = MojoNetworkTypeToOnc(properties->type);
-  if (onc_type.empty()) {
-    NET_LOG(ERROR) << "Invalid NetworkConfig properties";
-    return nullptr;
-  }
   auto onc = std::make_unique<base::DictionaryValue>();
 
-  // kType is a required / intrinsic property.
-  SetString(::onc::network_config::kType, onc_type, onc.get());
-
-  // Configurations have only one type dictionary, set below (unless empty).
+  // Process |properties->network_type| and set |type|. Configurations have only
+  // one type dictionary.
+  mojom::NetworkType type = mojom::NetworkType::kAll;  // Invalid type
   base::Value type_dict(base::Value::Type::DICTIONARY);
 
-  // Process remaining |properties| members. Order matches the mojo struct.
-
-  if (properties->auto_connect) {
-    NetworkTypePattern type = MojoTypeToPattern(properties->type);
-    if (type.Equals(NetworkTypePattern::Cellular()) ||
-        type.Equals(NetworkTypePattern::VPN()) ||
-        type.Equals(NetworkTypePattern::WiFi())) {
-      // Note: All type dicts use the same kAutoConnect key.
-      type_dict.SetBoolKey(::onc::wifi::kAutoConnect,
-                           properties->auto_connect->value);
-    }
-  }
-
-  if (properties->cellular) {
-    if (properties->cellular->apn) {
-      const mojom::ApnProperties& apn = *properties->cellular->apn;
+  if (properties->type_config->is_cellular()) {
+    type = mojom::NetworkType::kCellular;
+    const mojom::CellularConfigProperties& cellular =
+        *properties->type_config->get_cellular();
+    if (cellular.apn) {
+      const mojom::ApnProperties& apn = *cellular.apn;
       base::Value apn_dict(base::Value::Type::DICTIONARY);
       apn_dict.SetStringKey(::onc::cellular_apn::kAccessPointName,
                             apn.access_point_name);
@@ -1488,72 +1480,19 @@
       SetString(::onc::cellular_apn::kUsername, apn.username, &apn_dict);
       type_dict.SetKey(::onc::cellular::kAPN, std::move(apn_dict));
     }
-  }
-
-  if (properties->ethernet) {
-    const mojom::EthernetConfigProperties& ethernet = *properties->ethernet;
+  } else if (properties->type_config->is_ethernet()) {
+    type = mojom::NetworkType::kEthernet;
+    const mojom::EthernetConfigProperties& ethernet =
+        *properties->type_config->get_ethernet();
     SetString(::onc::ethernet::kAuthentication, ethernet.authentication,
               &type_dict);
     if (ethernet.eap) {
       type_dict.SetKey(::onc::ethernet::kEAP,
                        GetEAPProperties(*ethernet.eap.get()));
     }
-  }
-
-  if (properties->ip_address_config_type) {
-    onc->SetStringKey(::onc::network_config::kIPAddressConfigType,
-                      *properties->ip_address_config_type);
-  }
-
-  SetString(::onc::network_config::kName, properties->name, onc.get());
-
-  SetString(::onc::network_config::kNameServersConfigType,
-            properties->name_servers_config_type, onc.get());
-
-  if (properties->priority) {
-    onc->SetIntKey(::onc::network_config::kPriority,
-                   properties->priority->value);
-  }
-
-  if (properties->proxy_settings) {
-    const mojom::ProxySettings& proxy = *properties->proxy_settings;
-    base::Value proxy_dict(base::Value::Type::DICTIONARY);
-    proxy_dict.SetStringKey(::onc::proxy::kType, proxy.type);
-    if (proxy.manual) {
-      const mojom::ManualProxySettings& manual = *proxy.manual;
-      base::Value manual_dict(base::Value::Type::DICTIONARY);
-      SetProxyLocation(::onc::proxy::kHttp, manual.http_proxy, &manual_dict);
-      SetProxyLocation(::onc::proxy::kHttps, manual.secure_http_proxy,
-                       &manual_dict);
-      SetProxyLocation(::onc::proxy::kFtp, manual.ftp_proxy, &manual_dict);
-      SetProxyLocation(::onc::proxy::kSocks, manual.socks, &manual_dict);
-      proxy_dict.SetKey(::onc::proxy::kManual, std::move(manual_dict));
-    }
-    SetStringList(::onc::proxy::kExcludeDomains, proxy.exclude_domains,
-                  &proxy_dict);
-    SetString(::onc::proxy::kPAC, proxy.pac, &proxy_dict);
-    onc->SetKey(::onc::network_config::kProxySettings, std::move(proxy_dict));
-  }
-
-  if (properties->static_ip_config) {
-    const mojom::IPConfigProperties& ip_config = *properties->static_ip_config;
-    base::Value ip_config_dict(base::Value::Type::DICTIONARY);
-    SetString(::onc::ipconfig::kGateway, ip_config.gateway, &ip_config_dict);
-    SetString(::onc::ipconfig::kIPAddress, ip_config.ip_address,
-              &ip_config_dict);
-    SetStringList(::onc::ipconfig::kNameServers, ip_config.name_servers,
-                  &ip_config_dict);
-    ip_config_dict.SetIntKey(::onc::ipconfig::kRoutingPrefix,
-                             ip_config.routing_prefix);
-    SetString(::onc::ipconfig::kType, ip_config.type, &ip_config_dict);
-    SetString(::onc::ipconfig::kWebProxyAutoDiscoveryUrl,
-              ip_config.web_proxy_auto_discovery_url, &ip_config_dict);
-    onc->SetKey(::onc::network_config::kStaticIPConfig,
-                std::move(ip_config_dict));
-  }
-
-  if (properties->vpn) {
-    const mojom::VPNConfigProperties& vpn = *properties->vpn;
+  } else if (properties->type_config->is_vpn()) {
+    type = mojom::NetworkType::kVPN;
+    const mojom::VPNConfigProperties& vpn = *properties->type_config->get_vpn();
     SetString(::onc::vpn::kHost, vpn.host, &type_dict);
     if (vpn.ip_sec) {
       const mojom::IPSecConfigProperties& ip_sec = *vpn.ip_sec;
@@ -1609,12 +1548,12 @@
       type_dict.SetKey(::onc::vpn::kOpenVPN, std::move(open_vpn_dict));
     }
     SetString(::onc::vpn::kType, MojoVpnTypeToOnc(vpn.type), &type_dict);
-  }
-
-  if (properties->wifi) {
-    const mojom::WiFiConfigProperties& wifi = *properties->wifi;
+  } else if (properties->type_config->is_wifi()) {
+    type = mojom::NetworkType::kWiFi;
+    const mojom::WiFiConfigProperties& wifi =
+        *properties->type_config->get_wifi();
     SetString(::onc::wifi::kPassphrase, wifi.passphrase, &type_dict);
-    SetString(::onc::wifi::kSSID, wifi.ssid, &type_dict);
+    SetStringIfNotEmpty(::onc::wifi::kSSID, wifi.ssid, &type_dict);
     SetString(::onc::wifi::kPassphrase, wifi.passphrase, &type_dict);
     SetString(::onc::wifi::kSecurity, MojoSecurityTypeToOnc(wifi.security),
               &type_dict);
@@ -1623,6 +1562,78 @@
     }
   }
 
+  std::string onc_type = MojoNetworkTypeToOnc(type);
+  if (onc_type.empty()) {
+    NET_LOG(ERROR) << "Invalid NetworkConfig properties";
+    return nullptr;
+  }
+  SetString(::onc::network_config::kType, onc_type, onc.get());
+
+  // Process other |properties| members. Order matches the mojo struct.
+
+  if (properties->ip_address_config_type) {
+    onc->SetStringKey(::onc::network_config::kIPAddressConfigType,
+                      *properties->ip_address_config_type);
+  }
+
+  SetString(::onc::network_config::kName, properties->name, onc.get());
+
+  SetString(::onc::network_config::kNameServersConfigType,
+            properties->name_servers_config_type, onc.get());
+
+  if (properties->priority) {
+    onc->SetIntKey(::onc::network_config::kPriority,
+                   properties->priority->value);
+  }
+
+  if (properties->proxy_settings) {
+    const mojom::ProxySettings& proxy = *properties->proxy_settings;
+    base::Value proxy_dict(base::Value::Type::DICTIONARY);
+    proxy_dict.SetStringKey(::onc::proxy::kType, proxy.type);
+    if (proxy.manual) {
+      const mojom::ManualProxySettings& manual = *proxy.manual;
+      base::Value manual_dict(base::Value::Type::DICTIONARY);
+      SetProxyLocation(::onc::proxy::kHttp, manual.http_proxy, &manual_dict);
+      SetProxyLocation(::onc::proxy::kHttps, manual.secure_http_proxy,
+                       &manual_dict);
+      SetProxyLocation(::onc::proxy::kFtp, manual.ftp_proxy, &manual_dict);
+      SetProxyLocation(::onc::proxy::kSocks, manual.socks, &manual_dict);
+      proxy_dict.SetKey(::onc::proxy::kManual, std::move(manual_dict));
+    }
+    SetStringList(::onc::proxy::kExcludeDomains, proxy.exclude_domains,
+                  &proxy_dict);
+    SetString(::onc::proxy::kPAC, proxy.pac, &proxy_dict);
+    onc->SetKey(::onc::network_config::kProxySettings, std::move(proxy_dict));
+  }
+
+  if (properties->static_ip_config) {
+    const mojom::IPConfigProperties& ip_config = *properties->static_ip_config;
+    base::Value ip_config_dict(base::Value::Type::DICTIONARY);
+    SetString(::onc::ipconfig::kGateway, ip_config.gateway, &ip_config_dict);
+    SetString(::onc::ipconfig::kIPAddress, ip_config.ip_address,
+              &ip_config_dict);
+    SetStringList(::onc::ipconfig::kNameServers, ip_config.name_servers,
+                  &ip_config_dict);
+    ip_config_dict.SetIntKey(::onc::ipconfig::kRoutingPrefix,
+                             ip_config.routing_prefix);
+    SetString(::onc::ipconfig::kType, ip_config.type, &ip_config_dict);
+    SetString(::onc::ipconfig::kWebProxyAutoDiscoveryUrl,
+              ip_config.web_proxy_auto_discovery_url, &ip_config_dict);
+    onc->SetKey(::onc::network_config::kStaticIPConfig,
+                std::move(ip_config_dict));
+  }
+
+  if (properties->auto_connect) {
+    NetworkTypePattern type_pattern = MojoTypeToPattern(type);
+    if (type_pattern.Equals(NetworkTypePattern::Cellular()) ||
+        type_pattern.Equals(NetworkTypePattern::VPN()) ||
+        type_pattern.Equals(NetworkTypePattern::WiFi())) {
+      // Note: All type dicts use the same kAutoConnect key.
+      type_dict.SetBoolKey(::onc::wifi::kAutoConnect,
+                           properties->auto_connect->value);
+    }
+  }
+
   if (!type_dict.DictEmpty()) {
     onc->SetKey(onc_type, std::move(type_dict));
   }
@@ -1937,8 +1948,9 @@
   // apply the configuration to the EthernetEAP service. Currently we only
   // support configuration of EAP properties or other properties (e.g IP
   // Config), not both.
-  if (network->type() == shill::kTypeEthernet && properties->ethernet &&
-      properties->ethernet->eap) {
+  if (network->type() == shill::kTypeEthernet &&
+      properties->type_config->is_ethernet() &&
+      properties->type_config->get_ethernet()->eap) {
     const NetworkState* eap_state = network_state_handler_->GetEAPForEthernet(
         network->path(), /*connected_only=*/false);
     if (!eap_state) {
diff --git a/chromeos/services/network_config/cros_network_config_unittest.cc b/chromeos/services/network_config/cros_network_config_unittest.cc
index d77bc1c..683c3d0 100644
--- a/chromeos/services/network_config/cros_network_config_unittest.cc
+++ b/chromeos/services/network_config/cros_network_config_unittest.cc
@@ -740,7 +740,8 @@
 
   // Set priority.
   auto config = mojom::ConfigProperties::New();
-  config->type = mojom::NetworkType::kWiFi;
+  config->type_config = mojom::NetworkTypeConfigProperties::NewWifi(
+      mojom::WiFiConfigProperties::New());
   config->priority = mojom::PriorityConfig::New();
   config->priority->value = 1;
   bool success = SetProperties(kGUID, std::move(config));
@@ -757,7 +758,8 @@
 
   // Set auto connect only. Priority should remain unchanged.
   config = mojom::ConfigProperties::New();
-  config->type = mojom::NetworkType::kWiFi;
+  config->type_config = mojom::NetworkTypeConfigProperties::NewWifi(
+      mojom::WiFiConfigProperties::New());
   config->auto_connect = mojom::AutoConnectConfig::New();
   config->auto_connect->value = true;
   success = SetProperties(kGUID, std::move(config));
@@ -780,9 +782,10 @@
   const std::string ssid = "new_wifi_ssid";
   // Configure a new wifi network.
   auto config = mojom::ConfigProperties::New();
-  config->type = mojom::NetworkType::kWiFi;
-  config->wifi = mojom::WiFiConfigProperties::New();
-  config->wifi->ssid = ssid;
+  auto wifi = mojom::WiFiConfigProperties::New();
+  wifi->ssid = ssid;
+  config->type_config =
+      mojom::NetworkTypeConfigProperties::NewWifi(std::move(wifi));
   std::string guid = ConfigureNetwork(std::move(config), shared);
   EXPECT_FALSE(guid.empty());
 
diff --git a/chromeos/services/network_config/public/mojom/cros_network_config.mojom b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
index 68f86554..ad28ac0 100644
--- a/chromeos/services/network_config/public/mojom/cros_network_config.mojom
+++ b/chromeos/services/network_config/public/mojom/cros_network_config.mojom
@@ -758,10 +758,15 @@
   SecurityType security = kNone;
 };
 
+union NetworkTypeConfigProperties {
+  CellularConfigProperties cellular;
+  EthernetConfigProperties ethernet;
+  VPNConfigProperties vpn;
+  WiFiConfigProperties wifi;
+};
+
 struct ConfigProperties {
   AutoConnectConfig? auto_connect;
-  CellularConfigProperties? cellular;
-  EthernetConfigProperties? ethernet;
   string? ip_address_config_type;
   // Name is only required for new configurations.
   string? name;
@@ -769,10 +774,8 @@
   PriorityConfig? priority;
   ProxySettings? proxy_settings;
   IPConfigProperties? static_ip_config;
-  // |type| must match the existing configuration when used with SetProperties.
-  NetworkType type;
-  VPNConfigProperties? vpn;
-  WiFiConfigProperties? wifi;
+  // NetworkType is inferred from |type_config|.
+  NetworkTypeConfigProperties type_config;
 };
 
 // Properties provided to SetCellularSimState.
diff --git a/components/autofill/content/common/mojom/autofill_agent.mojom b/components/autofill/content/common/mojom/autofill_agent.mojom
index 4e8ff31..6bfd655 100644
--- a/components/autofill/content/common/mojom/autofill_agent.mojom
+++ b/components/autofill/content/common/mojom/autofill_agent.mojom
@@ -94,6 +94,10 @@
 
   // Informs the renderer that the Touch To Fill sheet has been dismissed.
   TouchToFillDismissed();
+
+  // Annotate password related (username, password) DOM input elements with
+  // corresponding HTML attributes. It is used only for debugging.
+  AnnotateFieldsWithParsingResult(ParsingResult parsing_result);
 };
 
 // There is one instance of this interface per render frame in the render
diff --git a/components/autofill/content/renderer/password_autofill_agent.cc b/components/autofill/content/renderer/password_autofill_agent.cc
index 37da9887..6e94315 100644
--- a/components/autofill/content/renderer/password_autofill_agent.cc
+++ b/components/autofill/content/renderer/password_autofill_agent.cc
@@ -72,6 +72,7 @@
 
 namespace autofill {
 
+using form_util::FindFormControlElementsByUniqueRendererId;
 using form_util::IsFormControlVisible;
 using form_util::IsFormVisible;
 
@@ -87,6 +88,7 @@
 // Names of HTML attributes to show form and field signatures for debugging.
 const char kDebugAttributeForFormSignature[] = "form_signature";
 const char kDebugAttributeForFieldSignature[] = "field_signature";
+const char kDebugAttributeForParserAnnotations[] = "pm_parser_annotation";
 
 // Maps element names to the actual elements to simplify form filling.
 typedef std::map<base::string16, WebInputElement> FormInputElementMap;
@@ -380,6 +382,19 @@
   return false;
 }
 
+void AnnotateFieldWithParsingResult(WebDocument doc,
+                                    uint32_t renderer_id,
+                                    const std::string& text) {
+  if (renderer_id == FormData::kNotSetFormRendererId)
+    return;
+  auto element = FindFormControlElementsByUniqueRendererId(doc, renderer_id);
+  if (element.IsNull())
+    return;
+  element.SetAttribute(
+      WebString::FromASCII(kDebugAttributeForParserAnnotations),
+      WebString::FromASCII(text));
+}
+
 }  // namespace
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1177,6 +1192,28 @@
                           logger.get());
 }
 
+void PasswordAutofillAgent::SetLoggingState(bool active) {
+  logging_state_active_ = active;
+}
+
+void PasswordAutofillAgent::TouchToFillDismissed() {
+  should_show_touch_to_fill_ = false;
+}
+
+void PasswordAutofillAgent::AnnotateFieldsWithParsingResult(
+    const ParsingResult& parsing_result) {
+  WebDocument doc = render_frame()->GetWebFrame()->GetDocument();
+  AnnotateFieldWithParsingResult(doc, parsing_result.username_renderer_id,
+                                 "username_element");
+  AnnotateFieldWithParsingResult(doc, parsing_result.password_renderer_id,
+                                 "password_element");
+  AnnotateFieldWithParsingResult(doc, parsing_result.new_password_renderer_id,
+                                 "new_password_element");
+  AnnotateFieldWithParsingResult(doc,
+                                 parsing_result.confirm_password_renderer_id,
+                                 "confirmation_password_element");
+}
+
 void PasswordAutofillAgent::FocusedNodeHasChanged(const blink::WebNode& node) {
   DCHECK(!node.IsNull());
   focused_input_element_.Reset();
@@ -1261,15 +1298,6 @@
       *web_frame, &field_data_manager_, &username_detector_cache_);
 }
 
-// mojom::PasswordAutofillAgent:
-void PasswordAutofillAgent::SetLoggingState(bool active) {
-  logging_state_active_ = active;
-}
-
-void PasswordAutofillAgent::TouchToFillDismissed() {
-  should_show_touch_to_fill_ = false;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // PasswordAutofillAgent, private:
 
diff --git a/components/autofill/content/renderer/password_autofill_agent.h b/components/autofill/content/renderer/password_autofill_agent.h
index 5b5b703..e5b5512 100644
--- a/components/autofill/content/renderer/password_autofill_agent.h
+++ b/components/autofill/content/renderer/password_autofill_agent.h
@@ -130,6 +130,8 @@
                             const base::string16& credential) override;
   void SetLoggingState(bool active) override;
   void TouchToFillDismissed() override;
+  void AnnotateFieldsWithParsingResult(
+      const ParsingResult& parsing_result) override;
 
   // FormTracker::Observer
   void OnProvisionallySaveForm(const blink::WebFormElement& form,
diff --git a/components/autofill/core/common/mojom/autofill_types.mojom b/components/autofill/core/common/mojom/autofill_types.mojom
index 2b26b1b..aececd0 100644
--- a/components/autofill/core/common/mojom/autofill_types.mojom
+++ b/components/autofill/core/common/mojom/autofill_types.mojom
@@ -294,3 +294,11 @@
   SubmissionIndicatorEvent submission_event;
   bool only_for_fallback;
 };
+
+// autofill::ParsingResult
+struct ParsingResult {
+  uint32 username_renderer_id;
+  uint32 password_renderer_id;
+  uint32 new_password_renderer_id;
+  uint32 confirm_password_renderer_id;
+};
diff --git a/components/autofill/core/common/mojom/autofill_types.typemap b/components/autofill/core/common/mojom/autofill_types.typemap
index 7d801ef..1531e37 100644
--- a/components/autofill/core/common/mojom/autofill_types.typemap
+++ b/components/autofill/core/common/mojom/autofill_types.typemap
@@ -31,6 +31,7 @@
   "autofill.mojom.FormFieldData=::autofill::FormFieldData",
   "autofill.mojom.FormFieldDataPredictions=::autofill::FormFieldDataPredictions",
   "autofill.mojom.FormsPredictionsMap=::autofill::FormsPredictionsMap",
+  "autofill.mojom.ParsingResult=::autofill::ParsingResult",
   "autofill.mojom.PasswordAndRealm=::autofill::PasswordAndRealm",
   "autofill.mojom.PasswordForm=::autofill::PasswordForm",
   "autofill.mojom.PasswordFormFieldPredictionMap=::autofill::PasswordFormFieldPredictionMap",
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc b/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
index dead49b..ff970d4 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits.cc
@@ -308,4 +308,16 @@
   return true;
 }
 
+bool StructTraits<
+    autofill::mojom::ParsingResultDataView,
+    autofill::ParsingResult>::Read(autofill::mojom::ParsingResultDataView data,
+                                   autofill::ParsingResult* out) {
+  out->username_renderer_id = data.username_renderer_id();
+  out->password_renderer_id = data.password_renderer_id();
+  out->new_password_renderer_id = data.new_password_renderer_id();
+  out->confirm_password_renderer_id = data.confirm_password_renderer_id();
+
+  return true;
+}
+
 }  // namespace mojo
diff --git a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
index 2b7b914..804c291 100644
--- a/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
+++ b/components/autofill/core/common/mojom/autofill_types_mojom_traits.h
@@ -591,6 +591,30 @@
                    autofill::ValueElementPair* out);
 };
 
+template <>
+struct StructTraits<autofill::mojom::ParsingResultDataView,
+                    autofill::ParsingResult> {
+  static uint32_t username_renderer_id(const autofill::ParsingResult& r) {
+    return r.username_renderer_id;
+  }
+
+  static uint32_t password_renderer_id(const autofill::ParsingResult& r) {
+    return r.password_renderer_id;
+  }
+
+  static uint32_t new_password_renderer_id(const autofill::ParsingResult& r) {
+    return r.new_password_renderer_id;
+  }
+
+  static uint32_t confirm_password_renderer_id(
+      const autofill::ParsingResult& r) {
+    return r.confirm_password_renderer_id;
+  }
+
+  static bool Read(autofill::mojom::ParsingResultDataView data,
+                   autofill::ParsingResult* out);
+};
+
 }  // namespace mojo
 
 #endif  // COMPONENTS_AUTOFILL_CORE_COMMON_MOJOM_AUTOFILL_TYPES_MOJOM_TRAITS_H_
diff --git a/components/autofill/core/common/password_form_fill_data.h b/components/autofill/core/common/password_form_fill_data.h
index 6b0f105..a94a1aee 100644
--- a/components/autofill/core/common/password_form_fill_data.h
+++ b/components/autofill/core/common/password_form_fill_data.h
@@ -13,6 +13,14 @@
 
 namespace autofill {
 
+// Contains renderer ids of password related elements found by the form parser.
+struct ParsingResult {
+  uint32_t username_renderer_id;
+  uint32_t password_renderer_id;
+  uint32_t new_password_renderer_id;
+  uint32_t confirm_password_renderer_id;
+};
+
 struct PasswordAndRealm {
   base::string16 password;
   std::string realm;
diff --git a/components/cronet/stale_host_resolver_unittest.cc b/components/cronet/stale_host_resolver_unittest.cc
index f489f86..454abaff 100644
--- a/components/cronet/stale_host_resolver_unittest.cc
+++ b/components/cronet/stale_host_resolver_unittest.cc
@@ -174,9 +174,12 @@
 
   std::unique_ptr<net::ContextHostResolver>
   CreateMockInnerResolverWithDnsClient(
-      std::unique_ptr<net::DnsClient> dns_client) {
+      std::unique_ptr<net::DnsClient> dns_client,
+      net::URLRequestContext* context = nullptr) {
     std::unique_ptr<net::ContextHostResolver> inner_resolver(
         net::HostResolver::CreateStandaloneContextResolver(nullptr));
+    if (context)
+      inner_resolver->SetRequestContext(context);
 
     net::ProcTaskParams proc_params(mock_proc_.get(), 1u);
     inner_resolver->SetProcParamsForTesting(proc_params);
@@ -209,10 +212,11 @@
     resolver_ = nullptr;
   }
 
-  void SetResolver(StaleHostResolver* stale_resolver) {
+  void SetResolver(StaleHostResolver* stale_resolver,
+                   net::URLRequestContext* context = nullptr) {
     DCHECK(!resolver_);
     stale_resolver->inner_resolver_ =
-        CreateMockInnerResolverWithDnsClient(nullptr);
+        CreateMockInnerResolverWithDnsClient(nullptr /* dns_client */, context);
     resolver_ = stale_resolver;
   }
 
@@ -696,7 +700,8 @@
   std::unique_ptr<net::URLRequestContext> context(builder.Build());
 
   // Experimental options ensure context's resolver is a StaleHostResolver.
-  SetResolver(reinterpret_cast<StaleHostResolver*>(context->host_resolver()));
+  SetResolver(reinterpret_cast<StaleHostResolver*>(context->host_resolver()),
+              context.get());
   // Note: Experimental config above sets 0ms stale delay.
   CreateCacheEntry(kAgeExpiredSec, net::OK);
 
diff --git a/components/dom_distiller/core/article_entry.cc b/components/dom_distiller/core/article_entry.cc
index d19d818..ff872d7 100644
--- a/components/dom_distiller/core/article_entry.cc
+++ b/components/dom_distiller/core/article_entry.cc
@@ -5,11 +5,6 @@
 #include "components/dom_distiller/core/article_entry.h"
 
 #include "base/logging.h"
-#include "components/sync/model/sync_change.h"
-
-using sync_pb::ArticlePage;
-using sync_pb::ArticleSpecifics;
-using sync_pb::EntitySpecifics;
 
 namespace dom_distiller {
 
@@ -33,39 +28,4 @@
   return left.SerializeAsString() == right.SerializeAsString();
 }
 
-ArticleEntry EntryFromSpecifics(const EntitySpecifics& specifics) {
-  DCHECK(specifics.has_article());
-  const ArticleSpecifics& article_specifics = specifics.article();
-  ArticleEntry entry = article_specifics;
-  DCHECK(IsEntryValid(entry));
-  return entry;
-}
-
-EntitySpecifics SpecificsFromEntry(const ArticleEntry& entry) {
-  DCHECK(IsEntryValid(entry));
-  EntitySpecifics specifics;
-  *specifics.mutable_article() = entry;
-  return specifics;
-}
-
-ArticleEntry GetEntryFromChange(const syncer::SyncChange& change) {
-  DCHECK(change.IsValid());
-  DCHECK(change.sync_data().IsValid());
-  return EntryFromSpecifics(change.sync_data().GetSpecifics());
-}
-
-std::string GetEntryIdFromSyncData(const syncer::SyncData& data) {
-  const EntitySpecifics& entity = data.GetSpecifics();
-  DCHECK(entity.has_article());
-  const ArticleSpecifics& specifics = entity.article();
-  DCHECK(specifics.has_entry_id());
-  return specifics.entry_id();
-}
-
-syncer::SyncData CreateLocalData(const ArticleEntry& entry) {
-  EntitySpecifics specifics = SpecificsFromEntry(entry);
-  const std::string& entry_id = entry.entry_id();
-  return syncer::SyncData::CreateLocalData(entry_id, entry_id, specifics);
-}
-
 }  // namespace dom_distiller
diff --git a/components/dom_distiller/core/article_entry.h b/components/dom_distiller/core/article_entry.h
index fd7228d..e23f9ff 100644
--- a/components/dom_distiller/core/article_entry.h
+++ b/components/dom_distiller/core/article_entry.h
@@ -5,16 +5,7 @@
 #ifndef COMPONENTS_DOM_DISTILLER_CORE_ARTICLE_ENTRY_H_
 #define COMPONENTS_DOM_DISTILLER_CORE_ARTICLE_ENTRY_H_
 
-#include <string>
-
-#include "components/dom_distiller/core/proto/distilled_article.pb.h"
-#include "components/sync/model/sync_data.h"
 #include "components/sync/protocol/article_specifics.pb.h"
-#include "components/sync/protocol/sync.pb.h"
-
-namespace syncer {
-class SyncChange;
-}
 
 namespace dom_distiller {
 
@@ -26,13 +17,6 @@
 
 bool AreEntriesEqual(const ArticleEntry& left, const ArticleEntry& right);
 
-sync_pb::EntitySpecifics SpecificsFromEntry(const ArticleEntry& entry);
-ArticleEntry EntryFromSpecifics(const sync_pb::EntitySpecifics& specifics);
-
-ArticleEntry GetEntryFromChange(const syncer::SyncChange& change);
-std::string GetEntryIdFromSyncData(const syncer::SyncData& data);
-syncer::SyncData CreateLocalData(const ArticleEntry& entry);
-
 }  // namespace dom_distiller
 
 #endif  // COMPONENTS_DOM_DISTILLER_CORE_ARTICLE_ENTRY_H_
diff --git a/components/dom_distiller/core/article_entry_unittest.cc b/components/dom_distiller/core/article_entry_unittest.cc
index 3d8ba0d7..62919a1 100644
--- a/components/dom_distiller/core/article_entry_unittest.cc
+++ b/components/dom_distiller/core/article_entry_unittest.cc
@@ -4,16 +4,8 @@
 
 #include "components/dom_distiller/core/article_entry.h"
 
-#include "components/sync/protocol/sync.pb.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-using sync_pb::ArticlePage;
-using sync_pb::ArticleSpecifics;
-using sync_pb::EntitySpecifics;
-using testing::AssertionFailure;
-using testing::AssertionResult;
-using testing::AssertionSuccess;
-
 namespace dom_distiller {
 
 TEST(DomDistillerArticleEntryTest, TestIsEntryValid) {
diff --git a/components/download/content/internal/download_driver_impl.cc b/components/download/content/internal/download_driver_impl.cc
index 6b81a071..6547ee0 100644
--- a/components/download/content/internal/download_driver_impl.cc
+++ b/components/download/content/internal/download_driver_impl.cc
@@ -183,7 +183,8 @@
   download_url_params->set_download_source(
       download::DownloadSource::INTERNAL_API);
   download_url_params->set_post_body(post_body);
-  download_url_params->set_follow_cross_origin_redirects(true);
+  download_url_params->set_cross_origin_redirects(
+      network::mojom::RedirectMode::kFollow);
   download_url_params->set_upload_progress_callback(
       base::BindRepeating(&DownloadDriverImpl::OnUploadProgress,
                           weak_ptr_factory_.GetWeakPtr(), guid));
diff --git a/components/download/internal/common/download_item_impl.cc b/components/download/internal/common/download_item_impl.cc
index 1c20ec0..44fb482 100644
--- a/components/download/internal/common/download_item_impl.cc
+++ b/components/download/internal/common/download_item_impl.cc
@@ -2437,7 +2437,8 @@
   // will only be sent to the URL returned by GetURL().
   download_params->set_referrer(GetReferrerUrl());
   download_params->set_referrer_policy(net::URLRequest::NEVER_CLEAR_REFERRER);
-  download_params->set_follow_cross_origin_redirects(false);
+  download_params->set_cross_origin_redirects(
+      network::mojom::RedirectMode::kError);
 
   TransitionTo(RESUMING_INTERNAL);
   RecordDownloadCountWithSource(source == ResumptionRequestSource::USER
diff --git a/components/download/internal/common/download_response_handler.cc b/components/download/internal/common/download_response_handler.cc
index dfbf59ea..8e09e3e 100644
--- a/components/download/internal/common/download_response_handler.cc
+++ b/components/download/internal/common/download_response_handler.cc
@@ -54,7 +54,7 @@
     bool is_parallel_request,
     bool is_transient,
     bool fetch_error_body,
-    bool follow_cross_origin_redirects,
+    network::mojom::RedirectMode cross_origin_redirects,
     const DownloadUrlParameters::RequestHeadersType& request_headers,
     const std::string& request_origin,
     DownloadSource download_source,
@@ -69,7 +69,7 @@
       referrer_policy_(resource_request->referrer_policy),
       is_transient_(is_transient),
       fetch_error_body_(fetch_error_body),
-      follow_cross_origin_redirects_(follow_cross_origin_redirects),
+      cross_origin_redirects_(cross_origin_redirects),
       first_origin_(url::Origin::Create(resource_request->url)),
       request_headers_(request_headers),
       request_origin_(request_origin),
@@ -165,16 +165,26 @@
     return;
   }
 
-  if (!follow_cross_origin_redirects_ &&
-      !first_origin_.IsSameOriginWith(
+  if (!first_origin_.IsSameOriginWith(
           url::Origin::Create(redirect_info.new_url))) {
-    abort_reason_ = DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT;
-    url_chain_.push_back(redirect_info.new_url);
-    method_ = redirect_info.new_method;
-    referrer_ = GURL(redirect_info.new_referrer);
-    referrer_policy_ = redirect_info.new_referrer_policy;
-    OnComplete(network::URLLoaderCompletionStatus(net::OK));
-    return;
+    // Cross-origin redirect.
+    switch (cross_origin_redirects_) {
+      case network::mojom::RedirectMode::kFollow:
+        // Pretend we didn't notice, and keep going.
+        break;
+      case network::mojom::RedirectMode::kManual:
+        abort_reason_ = DOWNLOAD_INTERRUPT_REASON_SERVER_CROSS_ORIGIN_REDIRECT;
+        url_chain_.push_back(redirect_info.new_url);
+        method_ = redirect_info.new_method;
+        referrer_ = GURL(redirect_info.new_referrer);
+        referrer_policy_ = redirect_info.new_referrer_policy;
+        OnComplete(network::URLLoaderCompletionStatus(net::OK));
+        return;
+      case network::mojom::RedirectMode::kError:
+        abort_reason_ = DOWNLOAD_INTERRUPT_REASON_NETWORK_INVALID_REQUEST;
+        OnComplete(network::URLLoaderCompletionStatus(net::OK));
+        return;
+    }
   }
 
   if (is_partial_request_) {
diff --git a/components/download/internal/common/parallel_download_job.cc b/components/download/internal/common/parallel_download_job.cc
index 9b92831..fbc67a6 100644
--- a/components/download/internal/common/parallel_download_job.cc
+++ b/components/download/internal/common/parallel_download_job.cc
@@ -285,8 +285,10 @@
   download_params->set_referrer_policy(net::URLRequest::NEVER_CLEAR_REFERRER);
 
   // TODO(xingliu): We should not support redirect at all for parallel requests.
-  // Currently the network service code path still can redirect.
-  download_params->set_follow_cross_origin_redirects(false);
+  // Currently the network service code path still can redirect as long as it's
+  // the same origin.
+  download_params->set_cross_origin_redirects(
+      network::mojom::RedirectMode::kError);
 
   // Send the request.
   worker->SendRequest(std::move(download_params),
diff --git a/components/download/internal/common/resource_downloader.cc b/components/download/internal/common/resource_downloader.cc
index cf8829e..1744d06f 100644
--- a/components/download/internal/common/resource_downloader.cc
+++ b/components/download/internal/common/resource_downloader.cc
@@ -157,7 +157,7 @@
           download_url_parameters->GetSaveInfo()),
       is_parallel_request, download_url_parameters->is_transient(),
       download_url_parameters->fetch_error_body(),
-      download_url_parameters->follow_cross_origin_redirects(),
+      download_url_parameters->cross_origin_redirects(),
       download_url_parameters->request_headers(),
       download_url_parameters->request_origin(),
       download_url_parameters->download_source(),
@@ -198,7 +198,7 @@
       false, /* is_parallel_request */
       false, /* is_transient */
       false, /* fetch_error_body */
-      true,  /* follow_cross_origin_redirects */
+      network::mojom::RedirectMode::kFollow,
       download::DownloadUrlParameters::RequestHeadersType(),
       std::string(), /* request_origin */
       download::DownloadSource::NAVIGATION, std::move(url_chain),
diff --git a/components/download/public/common/download_response_handler.h b/components/download/public/common/download_response_handler.h
index cf557de..0d8b3f2 100644
--- a/components/download/public/common/download_response_handler.h
+++ b/components/download/public/common/download_response_handler.h
@@ -49,7 +49,7 @@
       bool is_parallel_request,
       bool is_transient,
       bool fetch_error_body,
-      bool follow_cross_origin_redirects,
+      network::mojom::RedirectMode cross_origin_redirects,
       const DownloadUrlParameters::RequestHeadersType& request_headers,
       const std::string& request_origin,
       DownloadSource download_source,
@@ -91,7 +91,7 @@
   net::URLRequest::ReferrerPolicy referrer_policy_;
   bool is_transient_;
   bool fetch_error_body_;
-  bool follow_cross_origin_redirects_;
+  network::mojom::RedirectMode cross_origin_redirects_;
   url::Origin first_origin_;
   DownloadUrlParameters::RequestHeadersType request_headers_;
   std::string request_origin_;
diff --git a/components/download/public/common/download_url_parameters.cc b/components/download/public/common/download_url_parameters.cc
index fd465914..49ae91b2 100644
--- a/components/download/public/common/download_url_parameters.cc
+++ b/components/download/public/common/download_url_parameters.cc
@@ -35,7 +35,7 @@
       frame_tree_node_id_(-1),
       url_(url),
       do_not_prompt_for_login_(false),
-      follow_cross_origin_redirects_(true),
+      cross_origin_redirects_(network::mojom::RedirectMode::kFollow),
       fetch_error_body_(false),
       transient_(false),
       traffic_annotation_(traffic_annotation),
diff --git a/components/download/public/common/download_url_parameters.h b/components/download/public/common/download_url_parameters.h
index bf4d01902..6ae1a4b 100644
--- a/components/download/public/common/download_url_parameters.h
+++ b/components/download/public/common/download_url_parameters.h
@@ -22,6 +22,7 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request.h"
 #include "services/network/public/cpp/resource_request_body.h"
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "storage/browser/blob/blob_data_handle.h"
 #include "url/gurl.h"
 #include "url/origin.h"
@@ -204,11 +205,13 @@
     do_not_prompt_for_login_ = do_not_prompt;
   }
 
-  // If |follow_cross_origin_redirects| is true, we will follow cross origin
-  // redirects while downloading, otherwise, we'll attempt to navigate to the
-  // URL or cancel the download.
-  void set_follow_cross_origin_redirects(bool follow_cross_origin_redirects) {
-    follow_cross_origin_redirects_ = follow_cross_origin_redirects;
+  // If |cross_origin_redirects| is kFollow, we will follow cross origin
+  // redirects while downloading.  If it is kManual, then we'll attempt to
+  // navigate to the URL or cancel the download.  If it is kError, then we will
+  // fail the download (kFail).
+  void set_cross_origin_redirects(
+      network::mojom::RedirectMode cross_origin_redirects) {
+    cross_origin_redirects_ = cross_origin_redirects;
   }
 
   // Sets whether to download the response body even if the server returns
@@ -301,8 +304,8 @@
   bool prompt() const { return save_info_.prompt_for_save_location; }
   const GURL& url() const { return url_; }
   bool do_not_prompt_for_login() const { return do_not_prompt_for_login_; }
-  bool follow_cross_origin_redirects() const {
-    return follow_cross_origin_redirects_;
+  network::mojom::RedirectMode cross_origin_redirects() const {
+    return cross_origin_redirects_;
   }
   bool fetch_error_body() const { return fetch_error_body_; }
   bool is_transient() const { return transient_; }
@@ -349,7 +352,7 @@
   DownloadSaveInfo save_info_;
   GURL url_;
   bool do_not_prompt_for_login_;
-  bool follow_cross_origin_redirects_;
+  network::mojom::RedirectMode cross_origin_redirects_;
   bool fetch_error_body_;
   bool transient_;
   std::string guid_;
diff --git a/components/favicon/content/content_favicon_driver_unittest.cc b/components/favicon/content/content_favicon_driver_unittest.cc
index 85a955e..5dac5cea 100644
--- a/components/favicon/content/content_favicon_driver_unittest.cc
+++ b/components/favicon/content/content_favicon_driver_unittest.cc
@@ -103,7 +103,7 @@
 
 // Test that no download is initiated when DocumentOnLoadCompletedInMainFrame()
 // is not triggered (e.g. user stopped an ongoing page load).
-TEST_F(ContentFaviconDriverTest, DISABLED_ShouldNotCauseImageDownload) {
+TEST_F(ContentFaviconDriverTest, ShouldNotCauseImageDownload) {
   ContentFaviconDriver* favicon_driver =
       ContentFaviconDriver::FromWebContents(web_contents());
   web_contents_tester()->NavigateAndCommit(kPageURL);
@@ -120,8 +120,7 @@
 
 // Test that Favicon is not requested repeatedly during the same session if
 // the favicon is known to be unavailable (e.g. due to HTTP 404 status).
-TEST_F(ContentFaviconDriverTest,
-       DISABLED_ShouldNotRequestRepeatedlyIfUnavailable) {
+TEST_F(ContentFaviconDriverTest, ShouldNotRequestRepeatedlyIfUnavailable) {
   ON_CALL(favicon_service_, WasUnableToDownloadFavicon(kIconURL))
       .WillByDefault(Return(true));
   // Mimic a page load.
diff --git a/components/omnibox/browser/omnibox_edit_model.cc b/components/omnibox/browser/omnibox_edit_model.cc
index 7f9333fa..e6aa8f9 100644
--- a/components/omnibox/browser/omnibox_edit_model.cc
+++ b/components/omnibox/browser/omnibox_edit_model.cc
@@ -1124,7 +1124,7 @@
 
 bool OmniboxEditModel::OnEscapeKeyPressed() {
   if (has_temporary_text_) {
-    RevertTemporaryText(true);
+    RevertTemporaryTextAndPopup();
     return true;
   }
 
@@ -1184,7 +1184,7 @@
     // via user_input_in_progress_, which is false for ZeroSuggest.
     const size_t line_no = GetNewSelectedLine(count);
     if (has_temporary_text_ && line_no == 0 && user_input_in_progress_) {
-      RevertTemporaryText(true);
+      RevertTemporaryTextAndPopup();
     } else {
       popup_model()->MoveTo(line_no);
     }
@@ -1294,8 +1294,9 @@
     //
     // It may also be possible to reach here if we're reverting from having
     // temporary text back to a default match that's a keyword search, but in
-    // that case the RevertTemporaryText() call below will reset the caret or
-    // selection correctly so the caret positioning we do here won't matter.
+    // that case the RevertTemporaryTextAndPopup() call below will reset the
+    // caret or selection correctly so the caret positioning we do here won't
+    // matter.
     view_->SetWindowTextAndCaretPos(user_text, 0, false, true);
   } else if (view_->OnInlineAutocompleteTextMaybeChanged(
                  user_text + inline_autocomplete_text_, user_text.length())) {
@@ -1525,14 +1526,14 @@
   }
 }
 
-void OmniboxEditModel::RevertTemporaryText(bool revert_popup) {
+void OmniboxEditModel::RevertTemporaryTextAndPopup() {
   // The user typed something, then selected a different item.  Restore the
   // text they typed and change back to the default item.
   // NOTE: This purposefully does not reset paste_state_.
   just_deleted_text_ = false;
   has_temporary_text_ = false;
 
-  if (revert_popup && popup_model())
+  if (popup_model())
     popup_model()->ResetToDefaultMatch();
 
   const AutocompleteMatch& match = CurrentMatch(nullptr);
diff --git a/components/omnibox/browser/omnibox_edit_model.h b/components/omnibox/browser/omnibox_edit_model.h
index 1a18435c..588ed8b 100644
--- a/components/omnibox/browser/omnibox_edit_model.h
+++ b/components/omnibox/browser/omnibox_edit_model.h
@@ -435,8 +435,8 @@
                              GURL* alternate_nav_url) const;
 
   // Reverts the edit box from a temporary text back to the original user text.
-  // If |revert_popup| is true then the popup will be reverted as well.
-  void RevertTemporaryText(bool revert_popup);
+  // Also resets the popup to the default match.
+  void RevertTemporaryTextAndPopup();
 
   // Accepts current keyword if the user just typed a space at the end of
   // |new_text|.  This handles both of the following cases:
diff --git a/components/optimization_guide/command_line_top_host_provider.cc b/components/optimization_guide/command_line_top_host_provider.cc
index 7eb324b4..2231c79 100644
--- a/components/optimization_guide/command_line_top_host_provider.cc
+++ b/components/optimization_guide/command_line_top_host_provider.cc
@@ -29,21 +29,8 @@
 
 CommandLineTopHostProvider::~CommandLineTopHostProvider() = default;
 
-std::vector<std::string> CommandLineTopHostProvider::GetTopHosts(
-    size_t max_sites) {
-  if (top_hosts_.size() <= max_sites) {
-    return top_hosts_;
-  }
-
-  std::vector<std::string> top_hosts;
-  top_hosts.reserve(max_sites);
-  for (const auto& top_host : top_hosts_) {
-    if (top_hosts.size() >= max_sites)
-      return top_hosts;
-
-    top_hosts.push_back(top_host);
-  }
-  return top_hosts;
+std::vector<std::string> CommandLineTopHostProvider::GetTopHosts() {
+  return top_hosts_;
 }
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/command_line_top_host_provider.h b/components/optimization_guide/command_line_top_host_provider.h
index 949be27..b159670 100644
--- a/components/optimization_guide/command_line_top_host_provider.h
+++ b/components/optimization_guide/command_line_top_host_provider.h
@@ -25,7 +25,7 @@
   ~CommandLineTopHostProvider() override;
 
   // TopHostProvider implementation:
-  std::vector<std::string> GetTopHosts(size_t max_sites) override;
+  std::vector<std::string> GetTopHosts() override;
 
  private:
   explicit CommandLineTopHostProvider(
diff --git a/components/optimization_guide/command_line_top_host_provider_unittest.cc b/components/optimization_guide/command_line_top_host_provider_unittest.cc
index f7edda0..3b2255b2 100644
--- a/components/optimization_guide/command_line_top_host_provider_unittest.cc
+++ b/components/optimization_guide/command_line_top_host_provider_unittest.cc
@@ -38,8 +38,7 @@
   std::unique_ptr<CommandLineTopHostProvider> top_host_provider =
       CommandLineTopHostProvider::CreateIfEnabled();
   ASSERT_TRUE(top_host_provider);
-  std::vector<std::string> top_hosts =
-      top_host_provider->GetTopHosts(/*max_size=*/2);
+  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts();
   EXPECT_EQ(1ul, top_hosts.size());
   EXPECT_EQ("whatever.com", top_hosts[0]);
 }
@@ -52,10 +51,10 @@
   std::unique_ptr<CommandLineTopHostProvider> top_host_provider =
       CommandLineTopHostProvider::CreateIfEnabled();
   ASSERT_TRUE(top_host_provider);
-  std::vector<std::string> top_hosts =
-      top_host_provider->GetTopHosts(/*max_size=*/1);
-  EXPECT_EQ(1ul, top_hosts.size());
+  std::vector<std::string> top_hosts = top_host_provider->GetTopHosts();
+  EXPECT_EQ(2u, top_hosts.size());
   EXPECT_EQ("whatever.com", top_hosts[0]);
+  EXPECT_EQ("awesome.com", top_hosts[1]);
 }
 
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/hints_fetcher.cc b/components/optimization_guide/hints_fetcher.cc
index a9b80126..88223d0 100644
--- a/components/optimization_guide/hints_fetcher.cc
+++ b/components/optimization_guide/hints_fetcher.cc
@@ -103,9 +103,12 @@
   if (url_loader_)
     return false;
 
-  std::vector<std::string> filtered_hosts = GetHostsDueForHintsRefresh(hosts);
+  std::vector<std::string> filtered_hosts =
+      GetSizeLimitedHostsDueForHintsRefresh(hosts);
   if (filtered_hosts.empty())
     return false;
+  DCHECK_GE(features::MaxHostsForOptimizationGuideServiceHintsFetch(),
+            filtered_hosts.size());
 
   hints_fetch_start_time_ = base::TimeTicks::Now();
   request_context_ = request_context;
@@ -283,7 +286,7 @@
   url_loader_.reset();
 }
 
-std::vector<std::string> HintsFetcher::GetHostsDueForHintsRefresh(
+std::vector<std::string> HintsFetcher::GetSizeLimitedHostsDueForHintsRefresh(
     const std::vector<std::string>& hosts) const {
   SEQUENCE_CHECKER(sequence_checker_);
 
@@ -294,6 +297,9 @@
   target_hosts.reserve(hosts.size());
 
   for (const auto& host : hosts) {
+    // TODO(b/968542): Skip origins that are local hosts (e.g., IP addresses,
+    // localhost:8080 etc.).
+
     bool host_hints_due_for_refresh = true;
 
     base::Optional<double> value =
@@ -307,7 +313,14 @@
     }
     if (host_hints_due_for_refresh)
       target_hosts.push_back(host);
+
+    if (target_hosts.size() >=
+        features::MaxHostsForOptimizationGuideServiceHintsFetch()) {
+      break;
+    }
   }
+  DCHECK_GE(features::MaxHostsForOptimizationGuideServiceHintsFetch(),
+            target_hosts.size());
   return target_hosts;
 }
 
diff --git a/components/optimization_guide/hints_fetcher.h b/components/optimization_guide/hints_fetcher.h
index 6ee86c5..a222218 100644
--- a/components/optimization_guide/hints_fetcher.h
+++ b/components/optimization_guide/hints_fetcher.h
@@ -96,8 +96,9 @@
   void UpdateHostsSuccessfullyFetched();
 
   // Returns the subset of hosts from |hosts| for which the hints should be
-  // refreshed.
-  std::vector<std::string> GetHostsDueForHintsRefresh(
+  // refreshed. The count of returned hosts is limited to
+  // features::MaxHostsForOptimizationGuideServiceHintsFetch().
+  std::vector<std::string> GetSizeLimitedHostsDueForHintsRefresh(
       const std::vector<std::string>& hosts) const;
 
   // Used to hold the GetHintsRequest being constructed and sent as a remote
diff --git a/components/optimization_guide/hints_fetcher_unittest.cc b/components/optimization_guide/hints_fetcher_unittest.cc
index ad13417..8cb2738 100644
--- a/components/optimization_guide/hints_fetcher_unittest.cc
+++ b/components/optimization_guide/hints_fetcher_unittest.cc
@@ -78,6 +78,8 @@
         network::mojom::ConnectionType::CONNECTION_4G);
   }
 
+  // Updates the pref so that hints for each of the host in |hosts| are set to
+  // expire at |host_invalid_time|.
   void SeedCoveredHosts(const std::vector<std::string>& hosts,
                         base::Time host_invalid_time) {
     DictionaryPrefUpdate hosts_fetched(
@@ -450,7 +452,7 @@
   std::vector<std::string> hosts;
   size_t max_hosts =
       optimization_guide::features::MaxHostsForRecordingSuccessfullyCovered();
-  for (size_t i = 0; i < max_hosts - 1; i++) {
+  for (size_t i = 0; i < max_hosts - 1; ++i) {
     hosts.push_back("host" + base::NumberToString(i) + ".com");
   }
   base::Time host_expiry_time =
@@ -474,4 +476,34 @@
   EXPECT_TRUE(WasHostCoveredByFetch(extra_hosts[1]));
 }
 
+TEST_F(HintsFetcherTest, MaxHostsForOptimizationGuideServiceHintsFetch) {
+  base::HistogramTester histogram_tester;
+  std::string response_content;
+  std::vector<std::string> all_hosts;
+  size_t max_hosts_in_fetch_request = optimization_guide::features::
+      MaxHostsForOptimizationGuideServiceHintsFetch();
+  for (size_t i = 0; i < max_hosts_in_fetch_request; ++i) {
+    all_hosts.push_back("host" + base::NumberToString(i) + ".com");
+  }
+
+  all_hosts.push_back("extra1.com");
+  all_hosts.push_back("extra2.com");
+
+  EXPECT_TRUE(FetchHints(all_hosts));
+  VerifyHasPendingFetchRequests();
+  EXPECT_TRUE(SimulateResponse(response_content, net::HTTP_OK));
+  EXPECT_TRUE(hints_fetched());
+
+  DictionaryPrefUpdate hosts_fetched(
+      pref_service(), prefs::kHintsFetcherHostsSuccessfullyFetched);
+  EXPECT_EQ(max_hosts_in_fetch_request, hosts_fetched->size());
+  EXPECT_EQ(all_hosts.size(), max_hosts_in_fetch_request + 2);
+
+  for (size_t i = 0; i < all_hosts.size(); ++i) {
+    // Only the first |max_hosts_in_fetch_request| should be requested.
+    EXPECT_EQ(i < max_hosts_in_fetch_request,
+              WasHostCoveredByFetch(all_hosts[i]));
+  }
+}
+
 }  // namespace optimization_guide
diff --git a/components/optimization_guide/top_host_provider.h b/components/optimization_guide/top_host_provider.h
index d7a2b3c..ec278dd 100644
--- a/components/optimization_guide/top_host_provider.h
+++ b/components/optimization_guide/top_host_provider.h
@@ -15,9 +15,8 @@
  public:
   virtual ~TopHostProvider() {}
 
-  // Returns a vector of at most |max_sites| top hosts, the order of hosts is
-  // not guaranteed.
-  virtual std::vector<std::string> GetTopHosts(size_t max_sites) = 0;
+  // Returns a vector of at top hosts, the order of hosts is not guaranteed.
+  virtual std::vector<std::string> GetTopHosts() = 0;
 
  protected:
   TopHostProvider() {}
diff --git a/components/page_load_metrics/browser/BUILD.gn b/components/page_load_metrics/browser/BUILD.gn
index 0ca3f22..3dae2fc 100644
--- a/components/page_load_metrics/browser/BUILD.gn
+++ b/components/page_load_metrics/browser/BUILD.gn
@@ -45,6 +45,10 @@
 source_set("test_support") {
   testonly = true
   sources = [
+    "observers/page_load_metrics_observer_tester.cc",
+    "observers/page_load_metrics_observer_tester.h",
+    "page_load_metrics_test_content_browser_client.cc",
+    "page_load_metrics_test_content_browser_client.h",
     "page_load_metrics_test_waiter.cc",
     "page_load_metrics_test_waiter.h",
     "test_metrics_web_contents_observer_embedder.cc",
@@ -53,10 +57,13 @@
   deps = [
     ":browser",
     "//base",
+    "//base/test:test_support",
     "//components/page_load_metrics/common:page_load_metrics_mojom",
     "//components/page_load_metrics/common:test_support",
+    "//components/ukm:test_support",
     "//content/public/browser",
     "//content/public/common",
+    "//content/test:test_support",
     "//testing/gtest",
     "//ui/gfx/geometry",
   ]
@@ -66,6 +73,9 @@
   testonly = true
   sources = [
     "metrics_web_contents_observer_unittest.cc",
+    "observers/page_load_metrics_observer_content_test_harness.cc",
+    "observers/page_load_metrics_observer_content_test_harness.h",
+    "observers/use_counter_page_load_metrics_observer_unittest.cc",
     "page_load_metrics_util_unittest.cc",
     "resource_tracker_unittest.cc",
   ]
@@ -75,6 +85,7 @@
     "//base/test:test_support",
     "//components/page_load_metrics/common:page_load_metrics_mojom",
     "//components/page_load_metrics/common:test_support",
+    "//components/ukm/content",
     "//content/public/browser",
     "//content/test:test_support",
     "//testing/gtest",
diff --git a/components/page_load_metrics/browser/DEPS b/components/page_load_metrics/browser/DEPS
index 1e2b4a0..91c508d 100644
--- a/components/page_load_metrics/browser/DEPS
+++ b/components/page_load_metrics/browser/DEPS
@@ -3,6 +3,7 @@
   "+content/public/browser",
   "+content/public/test",
   "+components/data_reduction_proxy/core/browser",
+  "+components/ukm",
   "+net",
   "+services/metrics",
   "+third_party/blink/public",
diff --git a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
index 8a2af83..ee9c2f8 100644
--- a/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
+++ b/components/page_load_metrics/browser/metrics_web_contents_observer_unittest.cc
@@ -12,10 +12,9 @@
 #include "base/test/metrics/histogram_tester.h"
 #include "base/time/time.h"
 #include "base/timer/mock_timer.h"
-#include "components/page_load_metrics/browser/metrics_navigation_throttle.h"
+#include "components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "components/page_load_metrics/browser/test_metrics_web_contents_observer_embedder.h"
-#include "content/public/browser/content_browser_client.h"
 #include "content/public/browser/navigation_handle.h"
 #include "content/public/common/resource_load_info.mojom.h"
 #include "content/public/test/navigation_simulator.h"
@@ -60,24 +59,6 @@
   return resource_load_info;
 }
 
-class PageLoadMetricsTestContentBrowserClient
-    : public content::ContentBrowserClient {
- public:
-  PageLoadMetricsTestContentBrowserClient() = default;
-  ~PageLoadMetricsTestContentBrowserClient() override = default;
-
-  std::vector<std::unique_ptr<content::NavigationThrottle>>
-  CreateThrottlesForNavigation(
-      content::NavigationHandle* navigation_handle) override {
-    std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
-    if (navigation_handle->IsInMainFrame()) {
-      throttles.push_back(page_load_metrics::MetricsNavigationThrottle::Create(
-          navigation_handle));
-    }
-    return throttles;
-  }
-};
-
 }  //  namespace
 
 class MetricsWebContentsObserverTest
diff --git a/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc b/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc
new file mode 100644
index 0000000..1045990
--- /dev/null
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.cc
@@ -0,0 +1,47 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h"
+
+#include <string>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "components/ukm/content/source_url_recorder.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/common/content_client.h"
+#include "url/gurl.h"
+
+namespace page_load_metrics {
+
+PageLoadMetricsObserverContentTestHarness::
+    PageLoadMetricsObserverContentTestHarness()
+    : content::RenderViewHostTestHarness() {}
+
+PageLoadMetricsObserverContentTestHarness::
+    ~PageLoadMetricsObserverContentTestHarness() {}
+
+void PageLoadMetricsObserverContentTestHarness::SetUp() {
+  content::RenderViewHostTestHarness::SetUp();
+  original_browser_client_ =
+      content::SetBrowserClientForTesting(&browser_client_);
+  SetContents(CreateTestWebContents());
+  NavigateAndCommit(GURL("http://www.google.com"));
+  // Page load metrics depends on UKM source URLs being recorded, so make sure
+  // the SourceUrlRecorderWebContentsObserver is instantiated.
+  ukm::InitializeSourceUrlRecorderForWebContents(web_contents());
+  tester_ = std::make_unique<PageLoadMetricsObserverTester>(
+      web_contents(), this,
+      base::BindRepeating(
+          &PageLoadMetricsObserverContentTestHarness::RegisterObservers,
+          base::Unretained(this)));
+  web_contents()->WasShown();
+}
+
+void PageLoadMetricsObserverContentTestHarness::TearDown() {
+  content::SetBrowserClientForTesting(original_browser_client_);
+  content::RenderViewHostTestHarness::TearDown();
+}
+
+}  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h b/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h
new file mode 100644
index 0000000..1a13583
--- /dev/null
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h
@@ -0,0 +1,52 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_CONTENT_TEST_HARNESS_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_CONTENT_TEST_HARNESS_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "components/page_load_metrics/browser/metrics_navigation_throttle.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
+#include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+#include "components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h"
+#include "content/public/test/test_renderer_host.h"
+
+namespace page_load_metrics {
+
+class PageLoadTracker;
+
+// This class can be used to drive tests of PageLoadMetricsObservers in
+// components. To hook up an observer, override RegisterObservers and call
+// tracker->AddObserver. This will attach the observer to all main frame
+// navigations.
+//
+// Refer to PageLoadMetricsObserverTesterInterface for the methods the can be
+// used in test.
+class PageLoadMetricsObserverContentTestHarness
+    : public content::RenderViewHostTestHarness {
+ public:
+  PageLoadMetricsObserverContentTestHarness();
+  ~PageLoadMetricsObserverContentTestHarness() override;
+
+  void SetUp() override;
+  void TearDown() override;
+
+  virtual void RegisterObservers(PageLoadTracker* tracker) {}
+
+  PageLoadMetricsObserverTester* tester() { return tester_.get(); }
+  const PageLoadMetricsObserverTester* tester() const { return tester_.get(); }
+
+ private:
+  std::unique_ptr<PageLoadMetricsObserverTester> tester_;
+  PageLoadMetricsTestContentBrowserClient browser_client_;
+  content::ContentBrowserClient* original_browser_client_ = nullptr;
+
+  DISALLOW_COPY_AND_ASSIGN(PageLoadMetricsObserverContentTestHarness);
+};
+
+}  // namespace page_load_metrics
+
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_CONTENT_TEST_HARNESS_H_
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
similarity index 85%
rename from chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
rename to components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
index 7527d98..7582f44 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.cc
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h"
 
 #include <memory>
 #include <string>
@@ -10,8 +10,6 @@
 
 #include "base/time/time.h"
 #include "base/timer/timer.h"
-#include "chrome/browser/prerender/prerender_contents.h"
-#include "components/data_reduction_proxy/core/browser/data_reduction_proxy_data.h"
 #include "components/page_load_metrics/browser/metrics_web_contents_observer.h"
 #include "components/page_load_metrics/browser/page_load_metrics_embedder_interface.h"
 #include "content/public/browser/media_player_id.h"
@@ -22,16 +20,11 @@
 #include "content/public/common/resource_type.h"
 #include "content/public/test/navigation_simulator.h"
 #include "content/public/test/test_renderer_host.h"
-#include "extensions/buildflags/buildflags.h"
 #include "net/base/ip_endpoint.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/web_input_event.h"
 #include "url/gurl.h"
 
-#if BUILDFLAG(ENABLE_EXTENSIONS)
-#include "extensions/common/constants.h"
-#endif
-
 namespace page_load_metrics {
 
 namespace {
@@ -78,9 +71,10 @@
     : register_callback_(callback),
       web_contents_(web_contents),
       rfh_test_harness_(rfh_test_harness),
-      observer_(MetricsWebContentsObserver::CreateForWebContents(
-          web_contents,
-          std::make_unique<TestPageLoadMetricsEmbedderInterface>(this))) {}
+      metrics_web_contents_observer_(
+          MetricsWebContentsObserver::CreateForWebContents(
+              web_contents,
+              std::make_unique<TestPageLoadMetricsEmbedderInterface>(this))) {}
 
 PageLoadMetricsObserverTester::~PageLoadMetricsObserverTester() {}
 
@@ -188,7 +182,7 @@
     const mojom::CpuTiming& cpu_timing,
     const mojom::DeferredResourceCounts& new_deferred_resource_data,
     content::RenderFrameHost* rfh) {
-  observer_->OnTimingUpdated(
+  metrics_web_contents_observer_->OnTimingUpdated(
       rfh, timing.Clone(), metadata.Clone(), new_features.Clone(),
       std::vector<mojom::ResourceDataUpdatePtr>(), render_data.Clone(),
       cpu_timing.Clone(), new_deferred_resource_data.Clone());
@@ -210,13 +204,13 @@
     content::RenderFrameHost* render_frame_host) {
   auto timing = mojom::PageLoadTimingPtr(base::in_place);
   InitPageLoadTimingForTest(timing.get());
-  observer_->OnTimingUpdated(render_frame_host, std::move(timing),
-                             mojom::PageLoadMetadataPtr(base::in_place),
-                             mojom::PageLoadFeaturesPtr(base::in_place),
-                             resources,
-                             mojom::FrameRenderDataUpdatePtr(base::in_place),
-                             mojom::CpuTimingPtr(base::in_place),
-                             mojom::DeferredResourceCountsPtr(base::in_place));
+  metrics_web_contents_observer_->OnTimingUpdated(
+      render_frame_host, std::move(timing),
+      mojom::PageLoadMetadataPtr(base::in_place),
+      mojom::PageLoadFeaturesPtr(base::in_place), resources,
+      mojom::FrameRenderDataUpdatePtr(base::in_place),
+      mojom::CpuTimingPtr(base::in_place),
+      mojom::DeferredResourceCountsPtr(base::in_place));
 }
 
 void PageLoadMetricsObserverTester::SimulateLoadedResource(
@@ -247,30 +241,31 @@
   else
     resource_load_info.load_timing_info.request_start = base::TimeTicks::Now();
 
-  observer_->ResourceLoadComplete(web_contents()->GetMainFrame(), request_id,
-                                  resource_load_info);
+  metrics_web_contents_observer_->ResourceLoadComplete(
+      web_contents()->GetMainFrame(), request_id, resource_load_info);
 }
 
 void PageLoadMetricsObserverTester::SimulateFrameReceivedFirstUserActivation(
     content::RenderFrameHost* render_frame_host) {
-  observer_->FrameReceivedFirstUserActivation(render_frame_host);
+  metrics_web_contents_observer_->FrameReceivedFirstUserActivation(
+      render_frame_host);
 }
 
 void PageLoadMetricsObserverTester::SimulateInputEvent(
     const blink::WebInputEvent& event) {
-  observer_->OnInputEvent(event);
+  metrics_web_contents_observer_->OnInputEvent(event);
 }
 
 void PageLoadMetricsObserverTester::SimulateAppEnterBackground() {
-  observer_->FlushMetricsOnAppEnterBackground();
+  metrics_web_contents_observer_->FlushMetricsOnAppEnterBackground();
 }
 
 void PageLoadMetricsObserverTester::SimulateMediaPlayed() {
   content::WebContentsObserver::MediaPlayerInfo video_type(
       true /* has_video*/, true /* has_audio */);
   content::RenderFrameHost* render_frame_host = web_contents()->GetMainFrame();
-  observer_->MediaStartedPlaying(video_type,
-                                 content::MediaPlayerId(render_frame_host, 0));
+  metrics_web_contents_observer_->MediaStartedPlaying(
+      video_type, content::MediaPlayerId(render_frame_host, 0));
 }
 
 void PageLoadMetricsObserverTester::SimulateCookiesRead(
@@ -278,8 +273,8 @@
     const GURL& first_party_url,
     const net::CookieList& cookie_list,
     bool blocked_by_policy) {
-  observer_->OnCookiesRead(url, first_party_url, cookie_list,
-                           blocked_by_policy);
+  metrics_web_contents_observer_->OnCookiesRead(url, first_party_url,
+                                                cookie_list, blocked_by_policy);
 }
 
 void PageLoadMetricsObserverTester::SimulateCookieChange(
@@ -287,7 +282,8 @@
     const GURL& first_party_url,
     const net::CanonicalCookie& cookie,
     bool blocked_by_policy) {
-  observer_->OnCookieChange(url, first_party_url, cookie, blocked_by_policy);
+  metrics_web_contents_observer_->OnCookieChange(url, first_party_url, cookie,
+                                                 blocked_by_policy);
 }
 
 void PageLoadMetricsObserverTester::SimulateDomStorageAccess(
@@ -295,13 +291,13 @@
     const GURL& first_party_url,
     bool local,
     bool blocked_by_policy) {
-  observer_->OnDomStorageAccessed(url, first_party_url, local,
-                                  blocked_by_policy);
+  metrics_web_contents_observer_->OnDomStorageAccessed(
+      url, first_party_url, local, blocked_by_policy);
 }
 
 const PageLoadMetricsObserverDelegate&
 PageLoadMetricsObserverTester::GetDelegateForCommittedLoad() const {
-  return observer_->GetDelegateForCommittedLoad();
+  return metrics_web_contents_observer_->GetDelegateForCommittedLoad();
 }
 
 void PageLoadMetricsObserverTester::RegisterObservers(
diff --git a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
similarity index 87%
rename from chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h
rename to components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
index baae952..8838faca 100644
--- a/chrome/browser/page_load_metrics/observers/page_load_metrics_observer_tester.h
+++ b/components/page_load_metrics/browser/observers/page_load_metrics_observer_tester.h
@@ -2,35 +2,54 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-#ifndef CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_TESTER_H_
-#define CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_TESTER_H_
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_TESTER_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_TESTER_H_
+
+#include <vector>
 
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "components/page_load_metrics/browser/page_load_metrics_observer.h"
+#include "components/page_load_metrics/common/page_load_metrics.mojom.h"
 #include "components/page_load_metrics/common/test/weak_mock_timer.h"
 #include "components/ukm/test_ukm_recorder.h"
+#include "net/cookies/canonical_cookie.h"
+#include "ui/base/page_transition_types.h"
+
+namespace base {
+class GURL;
+class HistogramTester;
+}  // namespace base
 
 namespace blink {
 class WebInputEvent;
 }  // namespace blink
 
 namespace content {
-struct GlobalRequestID;
+class RenderFrameHost;
 class RenderViewHostTestHarness;
 class WebContents;
+struct GlobalRequestID;
 }  // namespace content
 
 namespace mojom {
+class FrameRenderDataUpdate;
+class PageLoadFeatures;
 class PageLoadMetadata;
 class PageLoadTiming;
 }  // namespace mojom
 
+namespace ukm {
+class TestAutoSetUkmRecorder;
+}  // namespace ukm
+
 namespace page_load_metrics {
 
 class MetricsWebContentsObserver;
+class PageLoadMetricsObserverDelegate;
 class PageLoadTracker;
+struct ExtraRequestCompleteInfo;
 
 // This class creates a MetricsWebContentsObserver and provides methods for
 // interacting with it. This class is designed to be used in unit tests for
@@ -127,7 +146,9 @@
                                 bool local,
                                 bool blocked_by_policy);
 
-  MetricsWebContentsObserver* observer() const { return observer_; }
+  MetricsWebContentsObserver* metrics_web_contents_observer() {
+    return metrics_web_contents_observer_;
+  }
   const base::HistogramTester& histogram_tester() const {
     return histogram_tester_;
   }
@@ -152,7 +173,7 @@
   RegisterObserversCallback register_callback_;
   content::WebContents* web_contents_;
   content::RenderViewHostTestHarness* rfh_test_harness_;
-  MetricsWebContentsObserver* observer_;
+  MetricsWebContentsObserver* metrics_web_contents_observer_;
   base::HistogramTester histogram_tester_;
   ukm::TestAutoSetUkmRecorder test_ukm_recorder_;
 
@@ -161,4 +182,4 @@
 
 }  // namespace page_load_metrics
 
-#endif  // CHROME_BROWSER_PAGE_LOAD_METRICS_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_TESTER_H_
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_OBSERVERS_PAGE_LOAD_METRICS_OBSERVER_TESTER_H_
diff --git a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
index 4732b94..65da30a9 100644
--- a/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
+++ b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer.cc
@@ -100,7 +100,8 @@
 
 }  // namespace
 
-UseCounterPageLoadMetricsObserver::UseCounterPageLoadMetricsObserver() {}
+UseCounterPageLoadMetricsObserver::UseCounterPageLoadMetricsObserver() =
+    default;
 
 UseCounterPageLoadMetricsObserver::~UseCounterPageLoadMetricsObserver() =
     default;
diff --git a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer_unittest.cc b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc
similarity index 82%
rename from chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer_unittest.cc
rename to components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc
index 89c2734..fb5bda54 100644
--- a/chrome/browser/page_load_metrics/observers/use_counter_page_load_metrics_observer_unittest.cc
+++ b/components/page_load_metrics/browser/observers/use_counter_page_load_metrics_observer_unittest.cc
@@ -6,10 +6,11 @@
 
 #include <memory>
 #include <vector>
+
 #include "base/macros.h"
 #include "base/metrics/histogram_base.h"
 #include "base/test/metrics/histogram_tester.h"
-#include "chrome/browser/page_load_metrics/observers/page_load_metrics_observer_test_harness.h"
+#include "components/page_load_metrics/browser/observers/page_load_metrics_observer_content_test_harness.h"
 #include "components/page_load_metrics/browser/page_load_tracker.h"
 #include "third_party/blink/public/mojom/web_feature/web_feature.mojom.h"
 #include "url/gurl.h"
@@ -23,7 +24,7 @@
 }  // namespace
 
 class UseCounterPageLoadMetricsObserverTest
-    : public page_load_metrics::PageLoadMetricsObserverTestHarness {
+    : public page_load_metrics::PageLoadMetricsObserverContentTestHarness {
  public:
   UseCounterPageLoadMetricsObserverTest() {}
 
@@ -32,45 +33,45 @@
       const page_load_metrics::mojom::PageLoadFeatures& second_features =
           page_load_metrics::mojom::PageLoadFeatures()) {
     NavigateAndCommit(GURL(kTestUrl));
-    SimulateFeaturesUpdate(first_features);
+    tester()->SimulateFeaturesUpdate(first_features);
     // Verify that kPageVisits is observed on commit.
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kFeaturesHistogramName,
         static_cast<base::Histogram::Sample>(WebFeature::kPageVisits), 1);
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kFeaturesHistogramMainFrameName,
         static_cast<base::Histogram::Sample>(WebFeature::kPageVisits), 1);
     // Verify that page visit is recorded for CSS histograms.
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kCssPropertiesHistogramName,
         blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kAnimatedCssPropertiesHistogramName,
         blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
 
     for (auto feature : first_features.features) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kFeaturesHistogramName,
           static_cast<base::Histogram::Sample>(feature), 1);
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kFeaturesHistogramMainFrameName,
           static_cast<base::Histogram::Sample>(feature), 1);
     }
 
-    SimulateFeaturesUpdate(second_features);
+    tester()->SimulateFeaturesUpdate(second_features);
     for (auto feature : first_features.features) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kFeaturesHistogramName,
           static_cast<base::Histogram::Sample>(feature), 1);
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kFeaturesHistogramMainFrameName,
           static_cast<base::Histogram::Sample>(feature), 1);
     }
     for (auto feature : second_features.features) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kFeaturesHistogramName,
           static_cast<base::Histogram::Sample>(feature), 1);
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kFeaturesHistogramMainFrameName,
           static_cast<base::Histogram::Sample>(feature), 1);
     }
@@ -81,24 +82,24 @@
       const page_load_metrics::mojom::PageLoadFeatures& second_features =
           page_load_metrics::mojom::PageLoadFeatures()) {
     NavigateAndCommit(GURL(kTestUrl));
-    SimulateFeaturesUpdate(first_features);
+    tester()->SimulateFeaturesUpdate(first_features);
     // Verify that page visit is recorded for CSS histograms.
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kCssPropertiesHistogramName,
         blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
 
     for (auto feature : first_features.css_properties) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kCssPropertiesHistogramName, feature, 1);
     }
 
-    SimulateFeaturesUpdate(second_features);
+    tester()->SimulateFeaturesUpdate(second_features);
     for (auto feature : first_features.css_properties) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kCssPropertiesHistogramName, feature, 1);
     }
     for (auto feature : second_features.css_properties) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kCssPropertiesHistogramName, feature, 1);
     }
   }
@@ -108,24 +109,24 @@
       const page_load_metrics::mojom::PageLoadFeatures& second_features =
           page_load_metrics::mojom::PageLoadFeatures()) {
     NavigateAndCommit(GURL(kTestUrl));
-    SimulateFeaturesUpdate(first_features);
+    tester()->SimulateFeaturesUpdate(first_features);
     // Verify that page visit is recorded for CSS histograms.
-    histogram_tester().ExpectBucketCount(
+    tester()->histogram_tester().ExpectBucketCount(
         internal::kAnimatedCssPropertiesHistogramName,
         blink::mojom::CSSSampleId::kTotalPagesMeasured, 1);
 
     for (auto feature : first_features.animated_css_properties) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kAnimatedCssPropertiesHistogramName, feature, 1);
     }
 
-    SimulateFeaturesUpdate(second_features);
+    tester()->SimulateFeaturesUpdate(second_features);
     for (auto feature : first_features.animated_css_properties) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kAnimatedCssPropertiesHistogramName, feature, 1);
     }
     for (auto feature : second_features.animated_css_properties) {
-      histogram_tester().ExpectBucketCount(
+      tester()->histogram_tester().ExpectBucketCount(
           internal::kAnimatedCssPropertiesHistogramName, feature, 1);
     }
   }
diff --git a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
new file mode 100644
index 0000000..6c6cfc5
--- /dev/null
+++ b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.cc
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h"
+
+#include <memory>
+#include <vector>
+
+#include "components/page_load_metrics/browser/metrics_navigation_throttle.h"
+#include "content/public/browser/navigation_handle.h"
+
+namespace page_load_metrics {
+
+PageLoadMetricsTestContentBrowserClient::
+    PageLoadMetricsTestContentBrowserClient() = default;
+
+PageLoadMetricsTestContentBrowserClient::
+    ~PageLoadMetricsTestContentBrowserClient() = default;
+
+std::vector<std::unique_ptr<content::NavigationThrottle>>
+PageLoadMetricsTestContentBrowserClient::CreateThrottlesForNavigation(
+    content::NavigationHandle* navigation_handle) {
+  std::vector<std::unique_ptr<content::NavigationThrottle>> throttles;
+  if (navigation_handle->IsInMainFrame()) {
+    throttles.push_back(page_load_metrics::MetricsNavigationThrottle::Create(
+        navigation_handle));
+  }
+  return throttles;
+}
+
+}  // namespace page_load_metrics
diff --git a/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h
new file mode 100644
index 0000000..484702f
--- /dev/null
+++ b/components/page_load_metrics/browser/page_load_metrics_test_content_browser_client.h
@@ -0,0 +1,32 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_TEST_CONTENT_BROWSER_CLIENT_H_
+#define COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_TEST_CONTENT_BROWSER_CLIENT_H_
+
+#include "base/macros.h"
+#include "content/public/browser/content_browser_client.h"
+
+namespace page_load_metrics {
+
+// The content::ContentBrowserClient should be used for page load metrics tests
+// in components.
+class PageLoadMetricsTestContentBrowserClient
+    : public content::ContentBrowserClient {
+ public:
+  PageLoadMetricsTestContentBrowserClient();
+  ~PageLoadMetricsTestContentBrowserClient() override;
+
+  // content::ContentBrowserClient:
+  std::vector<std::unique_ptr<content::NavigationThrottle>>
+  CreateThrottlesForNavigation(
+      content::NavigationHandle* navigation_handle) override;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(PageLoadMetricsTestContentBrowserClient);
+};
+
+}  // namespace page_load_metrics
+
+#endif  // COMPONENTS_PAGE_LOAD_METRICS_BROWSER_PAGE_LOAD_METRICS_TEST_CONTENT_BROWSER_CLIENT_H_
diff --git a/components/paint_preview/OWNERS b/components/paint_preview/OWNERS
index 1a9fee8..1fd924f 100644
--- a/components/paint_preview/OWNERS
+++ b/components/paint_preview/OWNERS
@@ -1,3 +1,4 @@
+ckitagawa@chromium.org
 mahmoudi@chromium.org
 vollick@chromium.org
 yfriedman@chromium.org
diff --git a/components/password_manager/content/browser/content_password_manager_driver.cc b/components/password_manager/content/browser/content_password_manager_driver.cc
index 4a24acc..19a3e3e 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver.cc
@@ -188,6 +188,11 @@
   return render_frame_host_->GetLastCommittedURL();
 }
 
+void ContentPasswordManagerDriver::AnnotateFieldsWithParsingResult(
+    const autofill::ParsingResult& parsing_result) {
+  GetPasswordAutofillAgent()->AnnotateFieldsWithParsingResult(parsing_result);
+}
+
 void ContentPasswordManagerDriver::GeneratePassword(
     autofill::mojom::PasswordGenerationAgent::
         UserTriggeredGeneratePasswordCallback callback) {
diff --git a/components/password_manager/content/browser/content_password_manager_driver.h b/components/password_manager/content/browser/content_password_manager_driver.h
index 39d1fde..af1acb2 100644
--- a/components/password_manager/content/browser/content_password_manager_driver.h
+++ b/components/password_manager/content/browser/content_password_manager_driver.h
@@ -77,6 +77,8 @@
   autofill::AutofillDriver* GetAutofillDriver() override;
   bool IsMainFrame() const override;
   const GURL& GetLastCommittedURL() const override;
+  void AnnotateFieldsWithParsingResult(
+      const autofill::ParsingResult& parsing_result) override;
 
   // Notify the renderer that the user wants to generate password manually.
   void GeneratePassword(autofill::mojom::PasswordGenerationAgent::
diff --git a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
index ae51e7d..f738737 100644
--- a/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
+++ b/components/password_manager/content/browser/content_password_manager_driver_unittest.cc
@@ -27,6 +27,7 @@
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/common/associated_interfaces/associated_interface_provider.h"
 
+using autofill::ParsingResult;
 using autofill::PasswordForm;
 using autofill::PasswordFormFillData;
 using base::ASCIIToUTF16;
@@ -82,9 +83,10 @@
   }
 
   // autofill::mojom::PasswordAutofillAgent:
-  MOCK_METHOD1(FillPasswordForm, void(const autofill::PasswordFormFillData&));
+  MOCK_METHOD1(FillPasswordForm, void(const PasswordFormFillData&));
   MOCK_METHOD2(FillIntoFocusedField, void(bool, const base::string16&));
   MOCK_METHOD0(TouchToFillDismissed, void());
+  MOCK_METHOD1(AnnotateFieldsWithParsingResult, void(const ParsingResult&));
 
   MOCK_METHOD0(BlacklistedFormFound, void());
 
diff --git a/components/password_manager/core/browser/password_form_filling.cc b/components/password_manager/core/browser/password_form_filling.cc
index fd47036..564182d 100644
--- a/components/password_manager/core/browser/password_form_filling.cc
+++ b/components/password_manager/core/browser/password_form_filling.cc
@@ -90,6 +90,16 @@
   DCHECK(driver);
   DCHECK_EQ(PasswordForm::Scheme::kHtml, observed_form.scheme);
 
+  if (autofill::IsShowAutofillSignaturesEnabled()) {
+    driver->AnnotateFieldsWithParsingResult(
+        {.username_renderer_id = observed_form.username_element_renderer_id,
+         .password_renderer_id = observed_form.password_element_renderer_id,
+         .new_password_renderer_id =
+             observed_form.new_password_element_renderer_id,
+         .confirm_password_renderer_id =
+             observed_form.confirmation_password_element_renderer_id});
+  }
+
   if (best_matches.empty()) {
     driver->InformNoSavedCredentials();
     metrics_recorder->RecordFillEvent(
diff --git a/components/password_manager/core/browser/password_manager_driver.h b/components/password_manager/core/browser/password_manager_driver.h
index 6e076a5..5cd02df2 100644
--- a/components/password_manager/core/browser/password_manager_driver.h
+++ b/components/password_manager/core/browser/password_manager_driver.h
@@ -104,6 +104,11 @@
   // Returns the last committed URL of the frame.
   virtual const GURL& GetLastCommittedURL() const = 0;
 
+  // Annotate password related (username, password) DOM input elements with
+  // corresponding HTML attributes. It is used only for debugging.
+  virtual void AnnotateFieldsWithParsingResult(
+      const autofill::ParsingResult& parsing_result) {}
+
  private:
   DISALLOW_COPY_AND_ASSIGN(PasswordManagerDriver);
 };
diff --git a/components/password_manager/core/browser/password_manager_onboarding.cc b/components/password_manager/core/browser/password_manager_onboarding.cc
index 4e977bcb..21b383b 100644
--- a/components/password_manager/core/browser/password_manager_onboarding.cc
+++ b/components/password_manager/core/browser/password_manager_onboarding.cc
@@ -119,16 +119,29 @@
   if (is_password_update) {
     return false;
   }
-  if (prefs->GetInteger(
-          password_manager::prefs::kPasswordManagerOnboardingState) ==
-      static_cast<int>(OnboardingState::kShouldShow)) {
-    // It is very important that the feature is checked last when we are certain
-    // that the onboarding needs to be shown. Otherwise, experiment data will be
-    // polluted.
-    return base::FeatureList::IsEnabled(
-        password_manager::features::kPasswordManagerOnboardingAndroid);
+
+  int pref_value = prefs->GetInteger(
+      password_manager::prefs::kPasswordManagerOnboardingState);
+
+  bool should_show =
+      (pref_value == static_cast<int>(OnboardingState::kShouldShow));
+  bool already_shown =
+      (pref_value == static_cast<int>(OnboardingState::kShown));
+  bool is_eligible = should_show || already_shown;
+
+  if (!is_eligible) {
+    return false;
   }
-  return false;
+
+  // It is very important that the feature is checked only for eligible users.
+  // otherwise the data will be diluted. It's also important that the feature is
+  // checked in all eligible cases, even if the onboarding prompt was shown,
+  // otherwise the data will be incomplete.
+  if (!base::FeatureList::IsEnabled(
+          password_manager::features::kPasswordManagerOnboardingAndroid)) {
+    return false;
+  }
+  return should_show;
 }
 
 SavingFlowMetricsRecorder::SavingFlowMetricsRecorder() = default;
diff --git a/components/pdf/renderer/pdf_accessibility_tree.cc b/components/pdf/renderer/pdf_accessibility_tree.cc
index 30d83460..292fa04 100644
--- a/components/pdf/renderer/pdf_accessibility_tree.cc
+++ b/components/pdf/renderer/pdf_accessibility_tree.cc
@@ -806,7 +806,7 @@
   content::RenderFrame* render_frame =
       host_->GetRenderFrameForInstance(instance_);
   DCHECK(render_frame);
-  return render_frame->GetRenderView()->GetDeviceScaleFactor();
+  return render_frame->GetDeviceScaleFactor();
 }
 
 content::RenderAccessibility* PdfAccessibilityTree::GetRenderAccessibility() {
diff --git a/components/previews/content/previews_decider_impl_unittest.cc b/components/previews/content/previews_decider_impl_unittest.cc
index 65cdffe..a6423e8 100644
--- a/components/previews/content/previews_decider_impl_unittest.cc
+++ b/components/previews/content/previews_decider_impl_unittest.cc
@@ -161,7 +161,7 @@
   TestTopHostProvider() {}
   ~TestTopHostProvider() override {}
 
-  std::vector<std::string> GetTopHosts(size_t max_sites) override {
+  std::vector<std::string> GetTopHosts() override {
     return std::vector<std::string>();
   }
 };
diff --git a/components/previews/content/previews_optimization_guide_impl.cc b/components/previews/content/previews_optimization_guide_impl.cc
index 8909e624..5c2095d 100644
--- a/components/previews/content/previews_optimization_guide_impl.cc
+++ b/components/previews/content/previews_optimization_guide_impl.cc
@@ -263,13 +263,8 @@
   base::Optional<std::vector<std::string>> top_hosts =
       optimization_guide::switches::ParseHintsFetchOverrideFromCommandLine();
   if (!top_hosts) {
-    top_hosts = top_host_provider_->GetTopHosts(
-        optimization_guide::features::
-            MaxHostsForOptimizationGuideServiceHintsFetch());
+    top_hosts = top_host_provider_->GetTopHosts();
   }
-  DCHECK_GE(optimization_guide::features::
-                MaxHostsForOptimizationGuideServiceHintsFetch(),
-            top_hosts->size());
 
   if (!hints_fetcher_) {
     hints_fetcher_ = std::make_unique<optimization_guide::HintsFetcher>(
diff --git a/components/previews/content/previews_optimization_guide_impl_unittest.cc b/components/previews/content/previews_optimization_guide_impl_unittest.cc
index e3564339..b1168ab8 100644
--- a/components/previews/content/previews_optimization_guide_impl_unittest.cc
+++ b/components/previews/content/previews_optimization_guide_impl_unittest.cc
@@ -101,7 +101,7 @@
 // A mock class implementation for unittesting previews_optimization_guide.
 class MockTopHostProvider : public optimization_guide::TopHostProvider {
  public:
-  MOCK_METHOD1(GetTopHosts, std::vector<std::string>(size_t max_sites));
+  MOCK_METHOD0(GetTopHosts, std::vector<std::string>());
 };
 
 std::unique_ptr<optimization_guide::proto::GetHintsResponse> BuildHintsResponse(
@@ -1890,7 +1890,7 @@
   guide()->SetHintsFetcherForTesting(
       BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
 
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(1);
+  EXPECT_CALL(*top_host_provider(), GetTopHosts()).Times(1);
 
   // Load hints so that OnHintsUpdated is called. This will force FetchHints to
   // be triggered if OptimizationHintsFetching is enabled.
@@ -1910,7 +1910,7 @@
 
   // This should be called exactly once, confirming that hints are not fetched
   // again after |kTestFetchRetryDelaySecs|.
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
+  EXPECT_CALL(*top_host_provider(), GetTopHosts())
       .Times(1)
       .WillRepeatedly(testing::Return(hosts));
 
@@ -1927,7 +1927,7 @@
   // Check that hints should not be fetched again after the delay for a failed
   // hints fetch attempt.
   MoveClockForwardBy(base::TimeDelta::FromSeconds(kTestFetchRetryDelaySecs));
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(0);
+  EXPECT_CALL(*top_host_provider(), GetTopHosts()).Times(0);
 }
 
 TEST_F(PreviewsOptimizationGuideImplTest, HintsFetcherEnabledWithHosts) {
@@ -1940,7 +1940,7 @@
       BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
 
   std::vector<std::string> hosts = {"example1.com", "example2.com"};
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
+  EXPECT_CALL(*top_host_provider(), GetTopHosts())
       .Times(1)
       .WillRepeatedly(testing::Return(hosts));
 
@@ -1964,7 +1964,7 @@
       BuildTestHintsFetcher(HintsFetcherEndState::kFetchFailed));
 
   std::vector<std::string> hosts = {"example1.com", "example2.com"};
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
+  EXPECT_CALL(*top_host_provider(), GetTopHosts())
       .Times(2)
       .WillRepeatedly(testing::Return(hosts));
 
@@ -1994,7 +1994,7 @@
       BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
 
   std::vector<std::string> hosts = {"example1.com", "example2.com"};
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
+  EXPECT_CALL(*top_host_provider(), GetTopHosts())
       .WillRepeatedly(testing::Return(hosts));
 
   // Force hints fetch scheduling.
@@ -2022,7 +2022,7 @@
   scoped_list.InitAndDisableFeature(
       optimization_guide::features::kOptimizationHintsFetching);
 
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(0);
+  EXPECT_CALL(*top_host_provider(), GetTopHosts()).Times(0);
   CreateServiceAndGuide();
   // Load hints so that OnHintsUpdated is called. This will
   // check that FetcHints is not triggered by making sure that top_host_provider
@@ -2049,7 +2049,7 @@
       BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
 
   std::vector<std::string> hosts = {"example1.com", "example2.com"};
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_))
+  EXPECT_CALL(*top_host_provider(), GetTopHosts())
       .Times(1)
       .WillRepeatedly(testing::Return(hosts));
 
@@ -2096,7 +2096,7 @@
       BuildTestHintsFetcher(HintsFetcherEndState::kFetchSuccessWithHints));
 
   std::vector<std::string> hosts = {"example1.com", "example2.com"};
-  EXPECT_CALL(*top_host_provider(), GetTopHosts(testing::_)).Times(0);
+  EXPECT_CALL(*top_host_provider(), GetTopHosts()).Times(0);
 
   // Load hints so that OnHintsUpdated is called. This will force FetchHints to
   // be triggered if OptimizationHintsFetching is enabled.
diff --git a/components/previews/core/previews_black_list.h b/components/previews/core/previews_black_list.h
index c7d297b..caba9c8 100644
--- a/components/previews/core/previews_black_list.h
+++ b/components/previews/core/previews_black_list.h
@@ -87,6 +87,10 @@
   NOT_ALLOWED_BY_OPTIMIZATION_GUIDE = 19,
   // The preview was not performed due to a coinflip experiment holdback.
   COINFLIP_HOLDBACK = 20,
+  // A redirect loop was detected.
+  REDIRECT_LOOP_DETECTED = 21,
+  // URL matched the deny list.
+  DENY_LIST_MATCHED = 22,
   LAST,
 };
 
diff --git a/components/previews/core/previews_logger.cc b/components/previews/core/previews_logger.cc
index d305a26..ee4ec4a 100644
--- a/components/previews/core/previews_logger.cc
+++ b/components/previews/core/previews_logger.cc
@@ -101,6 +101,12 @@
     case PreviewsEligibilityReason::COINFLIP_HOLDBACK:
       DCHECK(!want_inverse_description);
       return "Coin flip holdback encountered";
+    case PreviewsEligibilityReason::REDIRECT_LOOP_DETECTED:
+      DCHECK(!want_inverse_description);
+      return "Redirect loop detected";
+    case PreviewsEligibilityReason::DENY_LIST_MATCHED:
+      DCHECK(!want_inverse_description);
+      return "URL matched deny list";
     case PreviewsEligibilityReason::LAST:
       break;
   }
diff --git a/components/printing/renderer/print_render_frame_helper.cc b/components/printing/renderer/print_render_frame_helper.cc
index 7db6fdd..685d066 100644
--- a/components/printing/renderer/print_render_frame_helper.cc
+++ b/components/printing/renderer/print_render_frame_helper.cc
@@ -366,9 +366,8 @@
 }
 
 #if BUILDFLAG(ENABLE_PRINT_PREVIEW)
-// Returns true if the current destination printer is PRINT_TO_PDF.
 bool IsPrintToPdfRequested(const base::DictionaryValue& job_settings) {
-  return job_settings.FindBoolKey(kSettingPrintToPDF).value();
+  return job_settings.FindIntKey(kSettingPrinterType).value() == kPdfPrinter;
 }
 
 bool PrintingFrameHasPageSizeStyle(blink::WebLocalFrame* frame,
diff --git a/components/printing/test/print_mock_render_thread.cc b/components/printing/test/print_mock_render_thread.cc
index 8ee41919..1f8001f 100644
--- a/components/printing/test/print_mock_render_thread.cc
+++ b/components/printing/test/print_mock_render_thread.cc
@@ -134,7 +134,7 @@
       !job_settings.FindBoolKey(printing::kSettingLandscape) ||
       !job_settings.FindBoolKey(printing::kSettingCollate) ||
       !job_settings.FindIntKey(printing::kSettingColor) ||
-      !job_settings.FindBoolKey(printing::kSettingPrintToPDF) ||
+      !job_settings.FindIntKey(printing::kSettingPrinterType) ||
       !job_settings.FindBoolKey(printing::kIsFirstRequest) ||
       !job_settings.FindStringKey(printing::kSettingDeviceName) ||
       !job_settings.FindIntKey(printing::kSettingDuplexMode) ||
diff --git a/components/printing/test/print_render_frame_helper_browsertest.cc b/components/printing/test/print_render_frame_helper_browsertest.cc
index 2fe59f8..5469a90 100644
--- a/components/printing/test/print_render_frame_helper_browsertest.cc
+++ b/components/printing/test/print_render_frame_helper_browsertest.cc
@@ -118,7 +118,7 @@
   dict->SetBoolean(kSettingLandscape, false);
   dict->SetBoolean(kSettingCollate, false);
   dict->SetInteger(kSettingColor, GRAY);
-  dict->SetBoolean(kSettingPrintToPDF, true);
+  dict->SetInteger(kSettingPrinterType, kPdfPrinter);
   dict->SetInteger(kSettingDuplexMode, SIMPLEX);
   dict->SetInteger(kSettingCopies, 1);
   dict->SetString(kSettingDeviceName, "dummy");
@@ -799,7 +799,7 @@
   // Fill in some dummy values.
   base::DictionaryValue dict;
   CreatePrintSettingsDictionary(&dict);
-  dict.SetBoolean(kSettingPrintToPDF, false);
+  dict.SetInteger(kSettingPrinterType, kLocalPrinter);
   OnPrintPreview(dict);
 
   EXPECT_EQ(0, print_render_thread()->print_preview_pages_remaining());
@@ -821,7 +821,7 @@
   // Fill in some dummy values.
   base::DictionaryValue dict;
   CreatePrintSettingsDictionary(&dict);
-  dict.SetBoolean(kSettingPrintToPDF, false);
+  dict.SetInteger(kSettingPrinterType, kLocalPrinter);
   dict.SetInteger(kSettingMarginsType, NO_MARGINS);
   OnPrintPreview(dict);
 
@@ -968,7 +968,7 @@
   // Fill in some dummy values.
   base::DictionaryValue dict;
   CreatePrintSettingsDictionary(&dict);
-  dict.SetBoolean(kSettingPrintToPDF, false);
+  dict.SetInteger(kSettingPrinterType, kLocalPrinter);
   OnPrintPreview(dict);
 
   EXPECT_EQ(0, print_render_thread()->print_preview_pages_remaining());
@@ -1024,7 +1024,7 @@
   // Fill in some dummy values.
   base::DictionaryValue dict;
   CreatePrintSettingsDictionary(&dict);
-  dict.SetBoolean(kSettingPrintToPDF, false);
+  dict.SetInteger(kSettingPrinterType, kLocalPrinter);
   OnPrintPreview(dict);
 
   EXPECT_EQ(0, print_render_thread()->print_preview_pages_remaining());
@@ -1056,7 +1056,7 @@
   // Fill in some dummy values.
   base::DictionaryValue dict;
   CreatePrintSettingsDictionary(&dict);
-  dict.SetBoolean(kSettingPrintToPDF, false);
+  dict.SetInteger(kSettingPrinterType, kLocalPrinter);
   OnPrintPreview(dict);
 
   EXPECT_EQ(0, print_render_thread()->print_preview_pages_remaining());
@@ -1078,7 +1078,7 @@
   // Fill in some dummy values.
   base::DictionaryValue dict;
   CreatePrintSettingsDictionary(&dict);
-  dict.SetBoolean(kSettingPrintToPDF, false);
+  dict.SetInteger(kSettingPrinterType, kLocalPrinter);
   dict.SetInteger(kSettingMarginsType, NO_MARGINS);
   OnPrintPreview(dict);
 
diff --git a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
index 872d2a51..86df516 100644
--- a/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
+++ b/components/send_tab_to_self/send_tab_to_self_bridge_unittest.cc
@@ -96,6 +96,7 @@
     local_device_ = std::make_unique<syncer::DeviceInfo>(
         kLocalDeviceCacheGuid, "device", "72", "agent",
         sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+        base::SysInfo::HardwareInfo(),
         clock()->Now() - base::TimeDelta::FromDays(1),
         /*send_tab_to_self_receiving_enabled=*/true,
         /*sharing_info=*/base::nullopt);
@@ -649,6 +650,7 @@
   syncer::DeviceInfo recent_device(
       kRecentGuid, "device_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       clock()->Now() - base::TimeDelta::FromDays(1),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
@@ -657,6 +659,7 @@
   syncer::DeviceInfo old_device(
       kOldGuid, "device_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now() - base::TimeDelta::FromDays(3),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
@@ -665,6 +668,7 @@
   syncer::DeviceInfo older_device(
       kOlderGuid, "device_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now() - base::TimeDelta::FromDays(5),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
@@ -684,17 +688,19 @@
        GetTargetDeviceInfoSortedList_OnlyReceivingEnabled) {
   InitializeBridge();
 
-  syncer::DeviceInfo enabled_device(
-      "enabled_guid", "enabled_device_name", "72", "agent",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
-      /*last_updated_timestamp=*/clock()->Now(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  syncer::DeviceInfo enabled_device("enabled_guid", "enabled_device_name", "72",
+                                    "agent",
+                                    sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
+                                    "scoped_is", base::SysInfo::HardwareInfo(),
+                                    /*last_updated_timestamp=*/clock()->Now(),
+                                    /*send_tab_to_self_receiving_enabled=*/true,
+                                    /*sharing_info=*/base::nullopt);
   AddTestDevice(&enabled_device);
 
   syncer::DeviceInfo disabled_device(
       "disabled_guid", "disabled_device_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now(),
       /*send_tab_to_self_receiving_enabled=*/false,
       /*sharing_info=*/base::nullopt);
@@ -716,6 +722,7 @@
   syncer::DeviceInfo expired_device(
       "expired_guid", "expired_device_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now() - base::TimeDelta::FromDays(11),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
@@ -724,6 +731,7 @@
   syncer::DeviceInfo valid_device(
       "valid_guid", "valid_device_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now() - base::TimeDelta::FromDays(1),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
@@ -742,28 +750,31 @@
   InitializeBridge();
   bridge()->SetLocalDeviceNameForTest(kLocalDeviceName);
 
-  syncer::DeviceInfo local_device(
-      kLocalDeviceCacheGuid, kLocalDeviceName, "72", "agent",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
-      /*last_updated_timestamp=*/clock()->Now(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  syncer::DeviceInfo local_device(kLocalDeviceCacheGuid, kLocalDeviceName, "72",
+                                  "agent",
+                                  sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
+                                  "scoped_is", base::SysInfo::HardwareInfo(),
+                                  /*last_updated_timestamp=*/clock()->Now(),
+                                  /*send_tab_to_self_receiving_enabled=*/true,
+                                  /*sharing_info=*/base::nullopt);
   AddTestDevice(&local_device);
 
   syncer::DeviceInfo other_local_device(
       "other_local_guid", kLocalDeviceName, "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now(),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
   AddTestDevice(&local_device);
 
-  syncer::DeviceInfo other_device(
-      "other_guid", "other_device_name", "72", "agent",
-      sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
-      /*last_updated_timestamp=*/clock()->Now(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  syncer::DeviceInfo other_device("other_guid", "other_device_name", "72",
+                                  "agent",
+                                  sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
+                                  "scoped_is", base::SysInfo::HardwareInfo(),
+                                  /*last_updated_timestamp=*/clock()->Now(),
+                                  /*send_tab_to_self_receiving_enabled=*/true,
+                                  /*sharing_info=*/base::nullopt);
   AddTestDevice(&other_device);
 
   TargetDeviceInfo target_device_info(
@@ -783,6 +794,7 @@
   syncer::DeviceInfo older_device(
       "older_guid", "older_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now() - base::TimeDelta::FromDays(9),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
@@ -791,6 +803,7 @@
   syncer::DeviceInfo recent_device(
       "recent_guid", "recent_name", "72", "agent",
       sync_pb::SyncEnums_DeviceType_TYPE_LINUX, "scoped_is",
+      base::SysInfo::HardwareInfo(),
       /*last_updated_timestamp=*/clock()->Now() - base::TimeDelta::FromDays(1),
       /*send_tab_to_self_receiving_enabled=*/true,
       /*sharing_info=*/base::nullopt);
@@ -821,11 +834,12 @@
   InitializeBridge();
 
   // Set a valid device.
-  syncer::DeviceInfo device(
-      "guid", "name", "72", "agent", sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
-      "scoped_is", /*last_updated_timestamp=*/clock()->Now(),
-      /*send_tab_to_self_receiving_enabled=*/true,
-      /*sharing_info=*/base::nullopt);
+  syncer::DeviceInfo device("guid", "name", "72", "agent",
+                            sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
+                            "scoped_is", base::SysInfo::HardwareInfo(),
+                            /*last_updated_timestamp=*/clock()->Now(),
+                            /*send_tab_to_self_receiving_enabled=*/true,
+                            /*sharing_info=*/base::nullopt);
   AddTestDevice(&device);
 
   // Set the map by calling it. Make sure it has the device.
@@ -839,7 +853,7 @@
   // Add a new device.
   syncer::DeviceInfo new_device("new_guid", "new_name", "72", "agent",
                                 sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
-                                "scoped_is",
+                                "scoped_is", base::SysInfo::HardwareInfo(),
                                 /*last_updated_timestamp=*/clock()->Now(),
                                 /*send_tab_to_self_receiving_enabled=*/true,
                                 /*sharing_info=*/base::nullopt);
diff --git a/components/sync/model/sync_change.cc b/components/sync/model/sync_change.cc
index e182e4ef..8b56fbe 100644
--- a/components/sync/model/sync_change.cc
+++ b/components/sync/model/sync_change.cc
@@ -32,14 +32,8 @@
   // Local changes must always have a tag and specify a valid datatype.
   if (SyncDataLocal(sync_data_).GetTag().empty())
     return false;
-  // TODO(crbug.com/1007942): The ARTICLES data type has been removed and so is
-  // not considered a "real" data type anymore, but dom_distiller code still
-  // uses it for its local storage, and will DCHECK-fail if we don't consider
-  // article data as valid here.
-  if (!IsRealDataType(sync_data_.GetDataType()) &&
-      !sync_data_.GetSpecifics().has_article()) {
+  if (!IsRealDataType(sync_data_.GetDataType()))
     return false;
-  }
 
   // Adds and updates must have a non-unique-title.
   if (change_type_ == ACTION_ADD || change_type_ == ACTION_UPDATE)
diff --git a/components/sync/protocol/device_info_specifics.proto b/components/sync/protocol/device_info_specifics.proto
index b77163df3..8195d87 100644
--- a/components/sync/protocol/device_info_specifics.proto
+++ b/components/sync/protocol/device_info_specifics.proto
@@ -59,6 +59,12 @@
 
   // Device specific information for Sharing feature.
   optional SharingSpecificFields sharing_fields = 10;
+
+  // Model of device.
+  optional string model = 11;
+
+  // Name of device manufacturer.
+  optional string manufacturer = 12;
 }
 
 // Feature specific information about the device that is running a sync-enabled
diff --git a/components/sync/protocol/proto_visitors.h b/components/sync/protocol/proto_visitors.h
index 57601078..189a671 100644
--- a/components/sync/protocol/proto_visitors.h
+++ b/components/sync/protocol/proto_visitors.h
@@ -317,6 +317,8 @@
   VISIT(sync_user_agent);
   VISIT(chrome_version);
   VISIT(signin_scoped_device_id);
+  VISIT(model);
+  VISIT(manufacturer);
   VISIT(last_updated_timestamp);
   VISIT(feature_fields);
   VISIT(sharing_fields);
diff --git a/components/sync/protocol/sync.proto b/components/sync/protocol/sync.proto
index 20ab560..993becdc 100644
--- a/components/sync/protocol/sync.proto
+++ b/components/sync/protocol/sync.proto
@@ -22,7 +22,6 @@
 import "app_setting_specifics.proto";
 import "app_specifics.proto";
 import "arc_package_specifics.proto";
-import "article_specifics.proto";
 import "autofill_specifics.proto";
 import "bookmark_specifics.proto";
 import "client_commands.proto";
@@ -143,9 +142,6 @@
     FaviconImageSpecifics favicon_image = 182019;
     ManagedUserSettingSpecifics managed_user_setting = 186662;
     ManagedUserWhitelistSpecifics managed_user_whitelist = 306060;
-    // TODO(crbug.com/1007942): |article| isn't used by Sync anymore, but it
-    // can't be removed because dom_distiller code uses it for local storage.
-    ArticleSpecifics article = 223759 [deprecated = true];
     AppListSpecifics app_list = 229170;
     AutofillWalletSpecifics autofill_wallet = 306270;
     WalletMetadataSpecifics wallet_metadata = 330441;
@@ -166,6 +162,8 @@
   reserved "managed_user_shared_setting";
   reserved 218175;
   reserved "wifi_credential";
+  reserved 223759;
+  reserved "article";
 }
 
 message SyncEntity {
diff --git a/components/sync_device_info/device_info.cc b/components/sync_device_info/device_info.cc
index 2d4b3c7..b113c68 100644
--- a/components/sync_device_info/device_info.cc
+++ b/components/sync_device_info/device_info.cc
@@ -39,6 +39,7 @@
                        const std::string& sync_user_agent,
                        const sync_pb::SyncEnums::DeviceType device_type,
                        const std::string& signin_scoped_device_id,
+                       const base::SysInfo::HardwareInfo& hardware_info,
                        base::Time last_updated_timestamp,
                        bool send_tab_to_self_receiving_enabled,
                        const base::Optional<SharingInfo>& sharing_info)
@@ -48,6 +49,7 @@
       sync_user_agent_(sync_user_agent),
       device_type_(device_type),
       signin_scoped_device_id_(signin_scoped_device_id),
+      hardware_info_(hardware_info),
       last_updated_timestamp_(last_updated_timestamp),
       send_tab_to_self_receiving_enabled_(send_tab_to_self_receiving_enabled),
       sharing_info_(sharing_info) {}
@@ -82,6 +84,10 @@
   return signin_scoped_device_id_;
 }
 
+const base::SysInfo::HardwareInfo& DeviceInfo::hardware_info() const {
+  return hardware_info_;
+}
+
 base::Time DeviceInfo::last_updated_timestamp() const {
   return last_updated_timestamp_;
 }
diff --git a/components/sync_device_info/device_info.h b/components/sync_device_info/device_info.h
index 3e17461..ba69102 100644
--- a/components/sync_device_info/device_info.h
+++ b/components/sync_device_info/device_info.h
@@ -12,6 +12,7 @@
 #include "base/callback.h"
 #include "base/macros.h"
 #include "base/optional.h"
+#include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "components/sync/protocol/sync.pb.h"
 
@@ -56,6 +57,7 @@
              const std::string& sync_user_agent,
              const sync_pb::SyncEnums::DeviceType device_type,
              const std::string& signin_scoped_device_id,
+             const base::SysInfo::HardwareInfo& hardware_info,
              base::Time last_updated_timestamp,
              bool send_tab_to_self_receiving_enabled,
              const base::Optional<SharingInfo>& sharing_info);
@@ -88,6 +90,8 @@
   // annotating login scoped refresh token.
   const std::string& signin_scoped_device_id() const;
 
+  const base::SysInfo::HardwareInfo& hardware_info() const;
+
   // Returns the time at which this device was last updated to the sync servers.
   base::Time last_updated_timestamp() const;
 
@@ -139,6 +143,8 @@
   // and they are also reset when app/extension is uninstalled.
   std::string public_id_;
 
+  base::SysInfo::HardwareInfo hardware_info_;
+
   const base::Time last_updated_timestamp_;
 
   bool send_tab_to_self_receiving_enabled_;
diff --git a/components/sync_device_info/device_info_sync_bridge.cc b/components/sync_device_info/device_info_sync_bridge.cc
index d8a6336..b818707 100644
--- a/components/sync_device_info/device_info_sync_bridge.cc
+++ b/components/sync_device_info/device_info_sync_bridge.cc
@@ -74,11 +74,15 @@
 // Converts DeviceInfoSpecifics into a freshly allocated DeviceInfo.
 std::unique_ptr<DeviceInfo> SpecificsToModel(
     const DeviceInfoSpecifics& specifics) {
+  base::SysInfo::HardwareInfo hardware_info;
+  hardware_info.model = specifics.model();
+  hardware_info.manufacturer = specifics.manufacturer();
+
   return std::make_unique<DeviceInfo>(
       specifics.cache_guid(), specifics.client_name(),
       specifics.chrome_version(), specifics.sync_user_agent(),
       specifics.device_type(), specifics.signin_scoped_device_id(),
-      ProtoTimeToTime(specifics.last_updated_timestamp()),
+      hardware_info, ProtoTimeToTime(specifics.last_updated_timestamp()),
       specifics.feature_fields().send_tab_to_self_receiving_enabled(),
       SpecificsToSharingInfo(specifics));
 }
@@ -95,6 +99,7 @@
 // Converts a local DeviceInfo into a freshly allocated DeviceInfoSpecifics.
 std::unique_ptr<DeviceInfoSpecifics> MakeLocalDeviceSpecifics(
     const DeviceInfo& info) {
+  auto hardware_info = info.hardware_info();
   auto specifics = std::make_unique<DeviceInfoSpecifics>();
   specifics->set_cache_guid(info.guid());
   specifics->set_client_name(info.client_name());
@@ -102,6 +107,8 @@
   specifics->set_sync_user_agent(info.sync_user_agent());
   specifics->set_device_type(info.device_type());
   specifics->set_signin_scoped_device_id(info.signin_scoped_device_id());
+  specifics->set_model(hardware_info.model);
+  specifics->set_manufacturer(hardware_info.manufacturer);
   // The local device should have not been updated yet. Set the last updated
   // timestamp to now.
   DCHECK(info.last_updated_timestamp() == base::Time());
@@ -210,7 +217,8 @@
   DCHECK(!local_cache_guid_.empty());
 
   local_device_info_provider_->Initialize(local_cache_guid_,
-                                          local_personalizable_device_name_);
+                                          local_personalizable_device_name_,
+                                          local_hardware_info_);
 
   std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
   for (const auto& change : entity_data) {
@@ -399,6 +407,15 @@
 
   store_ = std::move(store);
 
+  base::SysInfo::GetHardwareInfo(
+      base::BindOnce(&DeviceInfoSyncBridge::OnHardwareInfoRetrieved,
+                     weak_ptr_factory_.GetWeakPtr()));
+}
+
+void DeviceInfoSyncBridge::OnHardwareInfoRetrieved(
+    base::SysInfo::HardwareInfo hardware_info) {
+  local_hardware_info_ = std::move(hardware_info);
+
   auto all_data = std::make_unique<ClientIdToSpecifics>();
   ClientIdToSpecifics* all_data_copy = all_data.get();
 
@@ -428,6 +445,7 @@
   }
 
   all_data_ = std::move(*all_data);
+
   local_personalizable_device_name_ =
       std::move(*local_personalizable_device_name);
 
@@ -481,7 +499,8 @@
   // initialize the provider immediately.
   local_cache_guid_ = local_cache_guid_in_metadata;
   local_device_info_provider_->Initialize(local_cache_guid_,
-                                          local_personalizable_device_name_);
+                                          local_personalizable_device_name_,
+                                          local_hardware_info_);
 
   // This probably isn't strictly needed, but in case the cache_guid has changed
   // we save the new one to prefs.
diff --git a/components/sync_device_info/device_info_sync_bridge.h b/components/sync_device_info/device_info_sync_bridge.h
index e4c76db..1e7fb12 100644
--- a/components/sync_device_info/device_info_sync_bridge.h
+++ b/components/sync_device_info/device_info_sync_bridge.h
@@ -14,6 +14,7 @@
 #include "base/memory/weak_ptr.h"
 #include "base/observer_list.h"
 #include "base/optional.h"
+#include "base/system/sys_info.h"
 #include "base/time/time.h"
 #include "base/timer/timer.h"
 #include "components/sync/model/model_error.h"
@@ -101,6 +102,7 @@
   // Methods used as callbacks given to DataTypeStore.
   void OnStoreCreated(const base::Optional<syncer::ModelError>& error,
                       std::unique_ptr<ModelTypeStore> store);
+  void OnHardwareInfoRetrieved(base::SysInfo::HardwareInfo hardware_info);
   void OnReadAllData(std::unique_ptr<ClientIdToSpecifics> all_data,
                      std::unique_ptr<std::string> session_name,
                      const base::Optional<syncer::ModelError>& error);
@@ -142,6 +144,7 @@
   std::string local_cache_guid_;
   std::string local_personalizable_device_name_;
   ClientIdToSpecifics all_data_;
+  base::SysInfo::HardwareInfo local_hardware_info_;
 
   // Registered observers, not owned.
   base::ObserverList<Observer, true>::Unchecked observers_;
diff --git a/components/sync_device_info/device_info_sync_bridge_unittest.cc b/components/sync_device_info/device_info_sync_bridge_unittest.cc
index 334cf79..1cb0031b 100644
--- a/components/sync_device_info/device_info_sync_bridge_unittest.cc
+++ b/components/sync_device_info/device_info_sync_bridge_unittest.cc
@@ -80,6 +80,8 @@
     }
   }
 
+  base::SysInfo::HardwareInfo hardware_info = arg.hardware_info();
+
   // Note that we ignore the device name here to avoid having to inject the
   // local device's.
   return expected_specifics.cache_guid() == arg.guid() &&
@@ -88,6 +90,8 @@
          expected_specifics.chrome_version() == arg.chrome_version() &&
          expected_specifics.signin_scoped_device_id() ==
              arg.signin_scoped_device_id() &&
+         expected_specifics.model() == hardware_info.model &&
+         expected_specifics.manufacturer() == hardware_info.manufacturer &&
          expected_specifics.feature_fields()
                  .send_tab_to_self_receiving_enabled() ==
              arg.send_tab_to_self_receiving_enabled();
@@ -136,6 +140,21 @@
   return base::StringPrintf("signin scoped device id %d", suffix);
 }
 
+std::string ModelForSuffix(int suffix) {
+  return base::StringPrintf("model %d", suffix);
+}
+
+std::string ManufacturerForSuffix(int suffix) {
+  return base::StringPrintf("manufacturer %d", suffix);
+}
+
+base::SysInfo::HardwareInfo HardwareInfoForSuffix(int suffix) {
+  base::SysInfo::HardwareInfo info;
+  info.manufacturer = ManufacturerForSuffix(suffix);
+  info.model = ModelForSuffix(suffix);
+  return info;
+}
+
 std::string SharingFcmTokenForSuffix(int suffix) {
   return base::StringPrintf("sharing fcm token %d", suffix);
 }
@@ -170,6 +189,8 @@
   specifics.set_sync_user_agent(SyncUserAgentForSuffix(suffix));
   specifics.set_chrome_version(ChromeVersionForSuffix(suffix));
   specifics.set_signin_scoped_device_id(SigninScopedDeviceIdForSuffix(suffix));
+  specifics.set_model(ModelForSuffix(suffix));
+  specifics.set_manufacturer(ManufacturerForSuffix(suffix));
   specifics.set_last_updated_timestamp(TimeToProtoTime(last_updated));
   specifics.mutable_feature_fields()->set_send_tab_to_self_receiving_enabled(
       true);
@@ -233,15 +254,20 @@
   ~TestLocalDeviceInfoProvider() override = default;
 
   // MutableLocalDeviceInfoProvider implementation.
+  // TODO(himanshujaju) - |hardware_info| is ignored right now. We could reuse
+  // it by calling GetHardwareInfo and storing locally before starting the
+  // tests.
   void Initialize(const std::string& cache_guid,
-                  const std::string& session_name) override {
+                  const std::string& session_name,
+                  const base::SysInfo::HardwareInfo& hardware_info) override {
     std::set<sync_pb::SharingSpecificFields::EnabledFeatures>
         sharing_enabled_features{SharingEnabledFeaturesForSuffix(kLocalSuffix)};
     local_device_info_ = std::make_unique<DeviceInfo>(
         cache_guid, session_name, ChromeVersionForSuffix(kLocalSuffix),
         SyncUserAgentForSuffix(kLocalSuffix),
         sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
-        SigninScopedDeviceIdForSuffix(kLocalSuffix), base::Time(),
+        SigninScopedDeviceIdForSuffix(kLocalSuffix),
+        HardwareInfoForSuffix(kLocalSuffix), base::Time(),
         /*send_tab_to_self_receiving_enabled=*/true,
         DeviceInfo::SharingInfo(SharingFcmTokenForSuffix(kLocalSuffix),
                                 SharingP256dhForSuffix(kLocalSuffix),
@@ -305,7 +331,7 @@
   // cause all initialization callbacks between the sevice and store to fire.
   void InitializeAndPump() {
     InitializeBridge();
-    base::RunLoop().RunUntilIdle();
+    task_environment_.RunUntilIdle();
   }
 
   // Creates the bridge with no prior data on the store, and mimics sync being
diff --git a/components/sync_device_info/fake_device_info_tracker.cc b/components/sync_device_info/fake_device_info_tracker.cc
index 8360aae5..f3b9bba 100644
--- a/components/sync_device_info/fake_device_info_tracker.cc
+++ b/components/sync_device_info/fake_device_info_tracker.cc
@@ -16,7 +16,7 @@
       device_info.guid(), device_info.client_name(),
       device_info.chrome_version(), device_info.sync_user_agent(),
       device_info.device_type(), device_info.signin_scoped_device_id(),
-      device_info.last_updated_timestamp(),
+      device_info.hardware_info(), device_info.last_updated_timestamp(),
       device_info.send_tab_to_self_receiving_enabled(),
       device_info.sharing_info());
 }
diff --git a/components/sync_device_info/fake_local_device_info_provider.cc b/components/sync_device_info/fake_local_device_info_provider.cc
index bb62a60..3c75320 100644
--- a/components/sync_device_info/fake_local_device_info_provider.cc
+++ b/components/sync_device_info/fake_local_device_info_provider.cc
@@ -4,20 +4,23 @@
 
 #include "components/sync_device_info/fake_local_device_info_provider.h"
 
+#include "base/system/sys_info.h"
 #include "base/time/time.h"
 
 namespace syncer {
 
 FakeLocalDeviceInfoProvider::FakeLocalDeviceInfoProvider()
-    : device_info_("id",
-                   "name",
-                   "chrome_version",
-                   "user_agent",
-                   sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
-                   "device_id",
-                   /*last_updated_timestamp=*/base::Time::Now(),
-                   /*send_tab_to_self_receiving_enabled=*/false,
-                   /*sharing_info=*/base::nullopt) {}
+    : device_info_(
+          "id",
+          "name",
+          "chrome_version",
+          "user_agent",
+          sync_pb::SyncEnums_DeviceType_TYPE_LINUX,
+          "device_id",
+          base::SysInfo::HardwareInfo{"model", "manufacturer", "serial"},
+          /*last_updated_timestamp=*/base::Time::Now(),
+          /*send_tab_to_self_receiving_enabled=*/false,
+          /*sharing_info=*/base::nullopt) {}
 
 FakeLocalDeviceInfoProvider::~FakeLocalDeviceInfoProvider() = default;
 
diff --git a/components/sync_device_info/local_device_info_provider.h b/components/sync_device_info/local_device_info_provider.h
index 7d9b394..1a6c368 100644
--- a/components/sync_device_info/local_device_info_provider.h
+++ b/components/sync_device_info/local_device_info_provider.h
@@ -9,6 +9,7 @@
 #include <string>
 
 #include "base/callback_list.h"
+#include "base/system/sys_info.h"
 #include "components/version_info/version_info.h"
 
 namespace syncer {
@@ -42,7 +43,8 @@
 class MutableLocalDeviceInfoProvider : public LocalDeviceInfoProvider {
  public:
   virtual void Initialize(const std::string& cache_guid,
-                          const std::string& session_name) = 0;
+                          const std::string& session_name,
+                          const base::SysInfo::HardwareInfo& hardware_info) = 0;
   virtual void Clear() = 0;
 };
 
diff --git a/components/sync_device_info/local_device_info_provider_impl.cc b/components/sync_device_info/local_device_info_provider_impl.cc
index 5574be15..a353a4e 100644
--- a/components/sync_device_info/local_device_info_provider_impl.cc
+++ b/components/sync_device_info/local_device_info_provider_impl.cc
@@ -50,8 +50,10 @@
   return callback_list_.Add(callback);
 }
 
-void LocalDeviceInfoProviderImpl::Initialize(const std::string& cache_guid,
-                                             const std::string& session_name) {
+void LocalDeviceInfoProviderImpl::Initialize(
+    const std::string& cache_guid,
+    const std::string& session_name,
+    const base::SysInfo::HardwareInfo& hardware_info) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
   DCHECK(!cache_guid.empty());
 
@@ -60,6 +62,7 @@
   local_device_info_ = std::make_unique<DeviceInfo>(
       cache_guid, session_name, version_, MakeUserAgentForSync(channel_),
       GetLocalDeviceType(), sync_client_->GetSigninScopedDeviceId(),
+      hardware_info,
       /*last_updated_timestamp=*/base::Time(),
       sync_client_->GetSendTabToSelfReceivingEnabled(),
       sync_client_->GetLocalSharingInfo());
diff --git a/components/sync_device_info/local_device_info_provider_impl.h b/components/sync_device_info/local_device_info_provider_impl.h
index 259dad5..fc7a0a4 100644
--- a/components/sync_device_info/local_device_info_provider_impl.h
+++ b/components/sync_device_info/local_device_info_provider_impl.h
@@ -12,6 +12,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "base/sequence_checker.h"
+#include "base/system/sys_info.h"
 #include "components/sync_device_info/device_info.h"
 #include "components/sync_device_info/local_device_info_provider.h"
 #include "components/version_info/version_info.h"
@@ -29,7 +30,8 @@
 
   // MutableLocalDeviceInfoProvider implementation.
   void Initialize(const std::string& cache_guid,
-                  const std::string& session_name) override;
+                  const std::string& session_name,
+                  const base::SysInfo::HardwareInfo& hardware_info) override;
   void Clear() override;
   version_info::Channel GetChannel() const override;
   const DeviceInfo* GetLocalDeviceInfo() const override;
diff --git a/components/sync_device_info/local_device_info_provider_impl_unittest.cc b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
index dc2b545..5b83650 100644
--- a/components/sync_device_info/local_device_info_provider_impl_unittest.cc
+++ b/components/sync_device_info/local_device_info_provider_impl_unittest.cc
@@ -58,7 +58,8 @@
   void InitializeProvider() { InitializeProvider(kLocalDeviceGuid); }
 
   void InitializeProvider(const std::string& guid) {
-    provider_->Initialize(guid, kLocalDeviceSessionName);
+    provider_->Initialize(guid, kLocalDeviceSessionName,
+                          base::SysInfo::HardwareInfo());
   }
 
   testing::NiceMock<MockDeviceInfoSyncClient> device_info_sync_client_;
diff --git a/components/sync_device_info/local_device_info_util.h b/components/sync_device_info/local_device_info_util.h
index 89770d4..6e51bc7 100644
--- a/components/sync_device_info/local_device_info_util.h
+++ b/components/sync_device_info/local_device_info_util.h
@@ -13,6 +13,8 @@
 
 sync_pb::SyncEnums::DeviceType GetLocalDeviceType();
 
+// Returns the personalizable device name. This may contain
+// personally-identifiable information - e.g. Alex's MacbookPro.
 std::string GetPersonalizableDeviceNameBlocking();
 
 }  // namespace syncer
diff --git a/components/viz/service/display_embedder/skia_output_surface_impl.cc b/components/viz/service/display_embedder/skia_output_surface_impl.cc
index 2e06d97..c34195b 100644
--- a/components/viz/service/display_embedder/skia_output_surface_impl.cc
+++ b/components/viz/service/display_embedder/skia_output_surface_impl.cc
@@ -685,7 +685,11 @@
   DCHECK(backend_format.isValid());
   auto characterization = gr_context_thread_safe->createCharacterization(
       cache_max_resource_bytes, image_info, backend_format, 0 /* sampleCount */,
-      kTopLeft_GrSurfaceOrigin, surface_props, mipmap);
+      kTopLeft_GrSurfaceOrigin, surface_props, mipmap,
+      false /* willUseGLFBO0 */, true /* isTextureable */,
+      impl_on_gpu_->GetGpuPreferences().enforce_vulkan_protected_memory
+          ? GrProtected::kYes
+          : GrProtected::kNo);
   DCHECK(characterization.isValid());
   return characterization;
 }
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 3ddb223..e6f870a 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -1085,8 +1085,6 @@
     "loader/cached_navigation_url_loader.h",
     "loader/cross_origin_read_blocking_checker.cc",
     "loader/cross_origin_read_blocking_checker.h",
-    "loader/data_pipe_to_source_stream.cc",
-    "loader/data_pipe_to_source_stream.h",
     "loader/download_utils_impl.cc",
     "loader/download_utils_impl.h",
     "loader/file_url_loader_factory.cc",
@@ -1109,8 +1107,6 @@
     "loader/shared_cors_origin_access_list_impl.h",
     "loader/single_request_url_loader_factory.cc",
     "loader/single_request_url_loader_factory.h",
-    "loader/source_stream_to_data_pipe.cc",
-    "loader/source_stream_to_data_pipe.h",
     "loader/webrtc_connections_observer.cc",
     "loader/webrtc_connections_observer.h",
     "locks/lock_manager.cc",
diff --git a/content/browser/download/download_browsertest.cc b/content/browser/download/download_browsertest.cc
index 2a9b205..a0a667f 100644
--- a/content/browser/download/download_browsertest.cc
+++ b/content/browser/download/download_browsertest.cc
@@ -1854,7 +1854,41 @@
   std::unique_ptr<DownloadTestObserver> observer(CreateWaiter(shell(), 1));
   auto download_parameters = std::make_unique<download::DownloadUrlParameters>(
       first_url, TRAFFIC_ANNOTATION_FOR_TESTS);
-  download_parameters->set_follow_cross_origin_redirects(true);
+  download_parameters->set_cross_origin_redirects(
+      network::mojom::RedirectMode::kFollow);
+  DownloadManagerForShell(shell())->DownloadUrl(std::move(download_parameters));
+  observer->WaitForFinished();
+
+  // Verify download failed.
+  std::vector<download::DownloadItem*> downloads;
+  DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
+  EXPECT_EQ(1u, downloads.size());
+  EXPECT_EQ(download::DownloadItem::COMPLETE, downloads[0]->GetState());
+}
+
+// Verify that DownloadUrl can detect and fail a cross-origin URL redirect.
+IN_PROC_BROWSER_TEST_F(DownloadContentTest, FailCrossOriginDownload) {
+  // Setup a cross-origin redirect chain with two URLs.
+  net::EmbeddedTestServer origin_one;
+  net::EmbeddedTestServer origin_two;
+  ASSERT_TRUE(origin_one.InitializeAndListen());
+  ASSERT_TRUE(origin_two.InitializeAndListen());
+
+  GURL first_url = origin_one.GetURL("/first-url");
+  GURL second_url = origin_two.GetURL("/download");
+
+  origin_one.ServeFilesFromDirectory(GetTestFilePath("download", ""));
+  origin_one.RegisterRequestHandler(
+      CreateRedirectHandler("/first-url", second_url));
+  origin_one.StartAcceptingConnections();
+  origin_two.StartAcceptingConnections();
+
+  // Start a download and explicitly specify to fail cross-origin redirect.
+  std::unique_ptr<DownloadTestObserver> observer(CreateWaiter(shell(), 1));
+  auto download_parameters = std::make_unique<download::DownloadUrlParameters>(
+      first_url, TRAFFIC_ANNOTATION_FOR_TESTS);
+  download_parameters->set_cross_origin_redirects(
+      network::mojom::RedirectMode::kError);
   DownloadManagerForShell(shell())->DownloadUrl(std::move(download_parameters));
   observer->WaitForFinished();
 
@@ -1862,7 +1896,10 @@
   std::vector<download::DownloadItem*> downloads;
   DownloadManagerForShell(shell())->GetAllDownloads(&downloads);
   EXPECT_EQ(1u, downloads.size());
-  EXPECT_EQ(download::DownloadItem::COMPLETE, downloads[0]->GetState());
+  EXPECT_EQ(download::DownloadItem::INTERRUPTED, downloads[0]->GetState());
+
+  ASSERT_TRUE(origin_two.ShutdownAndWaitUntilComplete());
+  ASSERT_TRUE(origin_one.ShutdownAndWaitUntilComplete());
 }
 
 // Verify that DownloadUrl() to URL with unsafe scheme should fail.
@@ -1886,7 +1923,8 @@
           DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL);
   auto download_parameters = std::make_unique<download::DownloadUrlParameters>(
       first_url, TRAFFIC_ANNOTATION_FOR_TESTS);
-  download_parameters->set_follow_cross_origin_redirects(true);
+  download_parameters->set_cross_origin_redirects(
+      network::mojom::RedirectMode::kFollow);
   download_manager->DownloadUrl(std::move(download_parameters));
   observer->WaitForFinished();
 
diff --git a/content/browser/frame_host/navigation_controller_impl.cc b/content/browser/frame_host/navigation_controller_impl.cc
index 6cf3ff8..2ab2b0c0 100644
--- a/content/browser/frame_host/navigation_controller_impl.cc
+++ b/content/browser/frame_host/navigation_controller_impl.cc
@@ -35,13 +35,13 @@
 
 #include "content/browser/frame_host/navigation_controller_impl.h"
 
+#include <algorithm>
 #include <utility>
 
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/logging.h"
 #include "base/metrics/histogram_macros.h"
-#include "base/strings/string_number_conversions.h"  // Temporary
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -58,7 +58,6 @@
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_request.h"
 #include "content/browser/frame_host/navigator.h"
-#include "content/browser/renderer_host/render_view_host_impl.h"  // Temporary
 #include "content/browser/site_instance_impl.h"
 #include "content/browser/web_package/bundled_exchanges_navigation_info.h"
 #include "content/common/content_constants_internal.h"
@@ -70,6 +69,7 @@
 #include "content/public/browser/navigation_details.h"
 #include "content/public/browser/notification_service.h"
 #include "content/public/browser/notification_types.h"
+#include "content/public/browser/render_view_host.h"
 #include "content/public/browser/render_widget_host.h"
 #include "content/public/browser/render_widget_host_view.h"
 #include "content/public/browser/replaced_navigation_entry_data.h"
@@ -77,6 +77,7 @@
 #include "content/public/common/content_client.h"
 #include "content/public/common/content_constants.h"
 #include "content/public/common/content_features.h"
+#include "content/public/common/url_constants.h"
 #include "content/public/common/url_utils.h"
 #include "media/base/mime_util.h"
 #include "net/base/escape.h"
@@ -108,11 +109,11 @@
 void ConfigureEntriesForRestore(
     std::vector<std::unique_ptr<NavigationEntryImpl>>* entries,
     RestoreType type) {
-  for (size_t i = 0; i < entries->size(); ++i) {
+  for (auto& entry : *entries) {
     // Use a transition type of reload so that we don't incorrectly increase
     // the typed count.
-    (*entries)[i]->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
-    (*entries)[i]->set_restore_type(type);
+    entry->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
+    entry->set_restore_type(type);
   }
 }
 
@@ -306,18 +307,15 @@
   }
 
   if (entry->restore_type() == RestoreType::LAST_SESSION_EXITED_CLEANLY) {
-    if (entry->GetHasPostData())
-      return mojom::NavigationType::RESTORE_WITH_POST;
-    else
-      return mojom::NavigationType::RESTORE;
+    return entry->GetHasPostData() ? mojom::NavigationType::RESTORE_WITH_POST
+                                   : mojom::NavigationType::RESTORE;
   }
 
   // History navigations.
   if (frame_entry.page_state().IsValid()) {
-    if (is_same_document_history_load)
-      return mojom::NavigationType::HISTORY_SAME_DOCUMENT;
-    else
-      return mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT;
+    return is_same_document_history_load
+               ? mojom::NavigationType::HISTORY_SAME_DOCUMENT
+               : mojom::NavigationType::HISTORY_DIFFERENT_DOCUMENT;
   }
   DCHECK(!is_same_document_history_load);
 
@@ -332,12 +330,10 @@
   // history navigation from 'A#foo' to 'A#bar' is not a same-document
   // navigation, but a different-document one. This is why history navigation
   // are classified before this check.
-  if (new_url.has_ref() && old_url.EqualsIgnoringRef(new_url) &&
-      frame_entry.method() == "GET") {
-    return mojom::NavigationType::SAME_DOCUMENT;
-  } else {
-    return mojom::NavigationType::DIFFERENT_DOCUMENT;
-  }
+  bool is_same_doc = new_url.has_ref() && old_url.EqualsIgnoringRef(new_url) &&
+                     frame_entry.method() == "GET";
+  return is_same_doc ? mojom::NavigationType::SAME_DOCUMENT
+                     : mojom::NavigationType::DIFFERENT_DOCUMENT;
 }
 
 // Adjusts the original input URL if needed, to get the URL to actually load and
@@ -510,19 +506,9 @@
     NavigationControllerDelegate* delegate,
     BrowserContext* browser_context)
     : browser_context_(browser_context),
-      pending_entry_(nullptr),
-      failed_pending_entry_id_(0),
-      last_committed_entry_index_(-1),
-      pending_entry_index_(-1),
-      transient_entry_index_(-1),
       delegate_(delegate),
       ssl_manager_(this),
-      needs_reload_(false),
-      is_initial_navigation_(true),
-      in_navigate_to_pending_entry_(false),
-      pending_reload_(ReloadType::NONE),
-      get_timestamp_callback_(base::BindRepeating(&base::Time::Now)),
-      entry_replaced_by_post_commit_error_(nullptr) {
+      get_timestamp_callback_(base::BindRepeating(&base::Time::Now)) {
   DCHECK(browser_context_);
 }
 
@@ -546,7 +532,8 @@
     RestoreType type,
     std::vector<std::unique_ptr<NavigationEntry>>* entries) {
   // Verify that this controller is unused and that the input is valid.
-  DCHECK(GetEntryCount() == 0 && !GetPendingEntry());
+  DCHECK_EQ(0, GetEntryCount());
+  DCHECK(!GetPendingEntry());
   DCHECK(selected_navigation >= 0 &&
          selected_navigation < static_cast<int>(entries->size()));
   DCHECK_EQ(-1, pending_entry_index_);
@@ -626,17 +613,18 @@
 
     pending_reload_ = reload_type;
     delegate_->ActivateAndShowRepostFormWarningDialog();
-  } else {
-    if (!IsInitialNavigation())
-      DiscardNonCommittedEntries();
-
-    pending_entry_ = entry;
-    pending_entry_index_ = current_index;
-    pending_entry_->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
-
-    NavigateToExistingPendingEntry(reload_type,
-                                   FrameTreeNode::kFrameTreeNodeInvalidId);
+    return;
   }
+
+  if (!IsInitialNavigation())
+    DiscardNonCommittedEntries();
+
+  pending_entry_ = entry;
+  pending_entry_index_ = current_index;
+  pending_entry_->SetTransitionType(ui::PAGE_TRANSITION_RELOAD);
+
+  NavigateToExistingPendingEntry(reload_type,
+                                 FrameTreeNode::kFrameTreeNodeInvalidId);
 }
 
 void NavigationControllerImpl::CancelPendingReload() {
@@ -906,7 +894,7 @@
 void NavigationControllerImpl::PruneForwardEntries() {
   DiscardNonCommittedEntries();
   int remove_start_index = last_committed_entry_index_ + 1;
-  int num_removed = int(entries_.size()) - remove_start_index;
+  int num_removed = static_cast<int>(entries_.size()) - remove_start_index;
   if (num_removed <= 0)
     return;
   entries_.erase(entries_.begin() + remove_start_index, entries_.end());
@@ -1243,13 +1231,12 @@
   if (rfh->GetParent()) {
     // All manual subframes would be did_create_new_entry and handled above, so
     // we know this is auto.
-    if (GetLastCommittedEntry()) {
+    if (GetLastCommittedEntry())
       return NAVIGATION_TYPE_AUTO_SUBFRAME;
-    } else {
-      // We ignore subframes created in non-committed pages; we'd appreciate if
-      // people stopped doing that.
-      return NAVIGATION_TYPE_NAV_IGNORE;
-    }
+
+    // We ignore subframes created in non-committed pages; we'd appreciate if
+    // people stopped doing that.
+    return NAVIGATION_TYPE_NAV_IGNORE;
   }
 
   if (params.nav_entry_id == 0) {
@@ -1663,8 +1650,7 @@
   // The site instance will normally be the same except
   // 1) session restore, when no site instance will be assigned or
   // 2) redirect, when the site instance is reset.
-  DCHECK(entry->site_instance() == nullptr ||
-         !entry->GetRedirectChain().empty() ||
+  DCHECK(!entry->site_instance() || !entry->GetRedirectChain().empty() ||
          entry->site_instance() == rfh->GetSiteInstance());
 
   // Update the existing FrameNavigationEntry to ensure all of its members
@@ -1950,7 +1936,8 @@
   NavigationControllerImpl* source =
       static_cast<NavigationControllerImpl*>(temp);
   // Verify that we look new.
-  DCHECK(GetEntryCount() == 0 && !GetPendingEntry());
+  DCHECK_EQ(0, GetEntryCount());
+  DCHECK(!GetPendingEntry());
 
   if (source->GetEntryCount() == 0)
     return;  // Nothing new to do.
@@ -2739,7 +2726,7 @@
   // a SiteInstance yet, in which case it will be assigned on first commit.
   if (!old_item ||
       new_item->item_sequence_number() != old_item->item_sequence_number() ||
-      (new_item->site_instance() != nullptr &&
+      (new_item->site_instance() &&
        new_item->site_instance() != old_item->site_instance())) {
     // Same document loads happen if the previous item has the same document
     // sequence number.  Note that we should treat them as different document if
@@ -2776,21 +2763,21 @@
       // For now, we accept this bug, and hope to resolve the race in a
       // different way that will one day allow us to fix this.
       return;
-    } else {
-      std::unique_ptr<NavigationRequest> navigation_request =
-          CreateNavigationRequestFromEntry(
-              frame, pending_entry_, new_item, reload_type,
-              false /* is_same_document_history_load */,
-              false /* is_history_navigation_in_new_child */);
-      if (navigation_request) {
-        // Only add the request if was properly created. It's possible for the
-        // creation to fail in certain cases, e.g. when the URL is invalid.
-        different_document_loads->push_back(std::move(navigation_request));
-      }
-      // For a different document, the subframes will be destroyed, so there's
-      // no need to consider them.
-      return;
     }
+
+    std::unique_ptr<NavigationRequest> navigation_request =
+        CreateNavigationRequestFromEntry(
+            frame, pending_entry_, new_item, reload_type,
+            false /* is_same_document_history_load */,
+            false /* is_history_navigation_in_new_child */);
+    if (navigation_request) {
+      // Only add the request if was properly created. It's possible for the
+      // creation to fail in certain cases, e.g. when the URL is invalid.
+      different_document_loads->push_back(std::move(navigation_request));
+    }
+    // For a different document, the subframes will be destroyed, so there's
+    // no need to consider them.
+    return;
   }
 
   for (size_t i = 0; i < frame->child_count(); i++) {
@@ -3188,8 +3175,7 @@
 #endif
           false, /* is_browser_initiated */
           network::mojom::IPAddressSpace::kUnknown,
-          GURL() /* base_url_override_for_bundled_exchanges */
-      );
+          GURL() /* base_url_override_for_bundled_exchanges */);
 #if defined(OS_ANDROID)
   if (ValidateDataURLAsString(params.data_url_as_string)) {
     commit_params->data_url_as_string = params.data_url_as_string->data();
diff --git a/content/browser/frame_host/navigation_controller_impl.h b/content/browser/frame_host/navigation_controller_impl.h
index 651d239..97c5935 100644
--- a/content/browser/frame_host/navigation_controller_impl.h
+++ b/content/browser/frame_host/navigation_controller_impl.h
@@ -8,7 +8,9 @@
 #include <stddef.h>
 #include <stdint.h>
 
+#include <memory>
 #include <set>
+#include <string>
 #include <vector>
 
 #include "base/callback.h"
@@ -60,7 +62,8 @@
   // each change to the pending entry
   class PendingEntryRef {
    public:
-    PendingEntryRef(base::WeakPtr<NavigationControllerImpl> controller);
+    explicit PendingEntryRef(
+        base::WeakPtr<NavigationControllerImpl> controller);
     ~PendingEntryRef();
 
    private:
@@ -535,7 +538,7 @@
   // ---------------------------------------------------------------------------
 
   // The user browser context associated with this controller.
-  BrowserContext* browser_context_;
+  BrowserContext* const browser_context_;
 
   // List of |NavigationEntry|s for this controller.
   std::vector<std::unique_ptr<NavigationEntryImpl>> entries_;
@@ -547,7 +550,7 @@
   // This may refer to an item in the entries_ list if the pending_entry_index_
   // != -1, or it may be its own entry that should be deleted. Be careful with
   // the memory management.
-  NavigationEntryImpl* pending_entry_;
+  NavigationEntryImpl* pending_entry_ = nullptr;
 
   // This keeps track of the NavigationRequests associated with the pending
   // NavigationEntry. When all of them have been deleted, or have stopped
@@ -565,21 +568,21 @@
   // issues: 1. This might hang around longer than we'd like if there is no
   // error page loaded, and 2. This doesn't work very well for frames.
   // http://crbug.com/474261
-  int failed_pending_entry_id_;
+  int failed_pending_entry_id_ = 0;
 
   // The index of the currently visible entry.
-  int last_committed_entry_index_;
+  int last_committed_entry_index_ = -1;
 
   // The index of the pending entry if it is in entries_, or -1 if
   // pending_entry_ is a new entry (created by LoadURL).
-  int pending_entry_index_;
+  int pending_entry_index_ = -1;
 
   // The index for the entry that is shown until a navigation occurs.  This is
   // used for interstitial pages. -1 if there are no such entry.
   // Note that this entry really appears in the list of entries, but only
   // temporarily (until the next navigation).  Any index pointing to an entry
   // after the transient entry will become invalid if you navigate forward.
-  int transient_entry_index_;
+  int transient_entry_index_ = -1;
 
   // The delegate associated with the controller. Possibly NULL during
   // setup.
@@ -589,7 +592,7 @@
   SSLManager ssl_manager_;
 
   // Whether we need to be reloaded when made active.
-  bool needs_reload_;
+  bool needs_reload_ = false;
 
   // Source of when |needs_reload_| is set. Only valid when |needs_reload_|
   // is set.
@@ -597,10 +600,10 @@
 
   // Whether this is the initial navigation.
   // Becomes false when initial navigation commits.
-  bool is_initial_navigation_;
+  bool is_initial_navigation_ = true;
 
   // Prevent unsafe re-entrant calls to NavigateToPendingEntry.
-  bool in_navigate_to_pending_entry_;
+  bool in_navigate_to_pending_entry_ = false;
 
   // Used to find the appropriate SessionStorageNamespace for the storage
   // partition of a NavigationEntry.
@@ -616,7 +619,7 @@
 
   // If a repost is pending, its type (RELOAD or RELOAD_BYPASSING_CACHE),
   // NO_RELOAD otherwise.
-  ReloadType pending_reload_;
+  ReloadType pending_reload_ = ReloadType::NONE;
 
   // Used to get timestamps for newly-created navigation entries.
   base::RepeatingCallback<base::Time()> get_timestamp_callback_;
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 553f51e..9e32310 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -3971,8 +3971,8 @@
     return;
 
   DownloadUrl(params.url, params.referrer, params.initiator_origin,
-              params.suggested_name, false,
-              params.follow_cross_origin_redirects, std::move(blob_url_token));
+              params.suggested_name, false, params.cross_origin_redirects,
+              std::move(blob_url_token));
 }
 
 void RenderFrameHostImpl::OnSaveImageFromDataURL(const std::string& url_str) {
@@ -3984,8 +3984,8 @@
   if (!data_url.is_valid() || !data_url.SchemeIs(url::kDataScheme))
     return;
 
-  DownloadUrl(data_url, Referrer(), url::Origin(), base::string16(), true, true,
-              mojo::NullRemote());
+  DownloadUrl(data_url, Referrer(), url::Origin(), base::string16(), true,
+              network::mojom::RedirectMode::kFollow, mojo::NullRemote());
 }
 
 void RenderFrameHostImpl::DownloadUrl(
@@ -3994,7 +3994,7 @@
     const url::Origin& initiator,
     const base::string16& suggested_name,
     const bool use_prompt,
-    const bool follow_cross_origin_redirects,
+    const network::mojom::RedirectMode cross_origin_redirects,
     mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token) {
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("renderer_initiated_download", R"(
@@ -4028,7 +4028,7 @@
   parameters->set_content_initiated(true);
   parameters->set_suggested_name(suggested_name);
   parameters->set_prompt(use_prompt);
-  parameters->set_follow_cross_origin_redirects(follow_cross_origin_redirects);
+  parameters->set_cross_origin_redirects(cross_origin_redirects);
   parameters->set_referrer(referrer.url);
   parameters->set_referrer_policy(
       Referrer::ReferrerPolicyForUrlRequest(referrer.policy));
diff --git a/content/browser/frame_host/render_frame_host_impl.h b/content/browser/frame_host/render_frame_host_impl.h
index 3e2a5a3..af4f6aa 100644
--- a/content/browser/frame_host/render_frame_host_impl.h
+++ b/content/browser/frame_host/render_frame_host_impl.h
@@ -1917,7 +1917,7 @@
       const url::Origin& initiator,
       const base::string16& suggested_name,
       const bool use_prompt,
-      const bool follow_cross_origin_redirects,
+      const network::mojom::RedirectMode cross_origin_redirects,
       mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token);
 
   // The RenderViewHost that this RenderFrameHost is associated with.
diff --git a/content/browser/frame_host/render_frame_host_impl_browsertest.cc b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
index 215c16d..484f198 100644
--- a/content/browser/frame_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/frame_host/render_frame_host_impl_browsertest.cc
@@ -3043,12 +3043,21 @@
 
 }  // namespace
 
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ComputeMainFrameIPAddressSpace) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      network::features::kBlockNonSecureExternalRequests);
+class RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked
+    : public RenderFrameHostImplBrowserTest {
+ public:
+  RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked() {
+    feature_list_.InitAndEnableFeature(
+        network::features::kBlockNonSecureExternalRequests);
+  }
 
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(
+    RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked,
+    ComputeMainFrameIPAddressSpace) {
   // TODO(mkwst): `about:`, `file:`, `data:`, `blob:`, and `filesystem:` URLs
   // are all treated as `kUnknown` today. This is ~incorrect, but safe, as their
   // web-facing behavior will be equivalent to "public".
@@ -3079,12 +3088,9 @@
   }
 }
 
-IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       ComputeIFrameLoopbackIPAddressSpace) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      network::features::kBlockNonSecureExternalRequests);
-
+IN_PROC_BROWSER_TEST_F(
+    RenderFrameHostImplBrowserTestWithNonSecureExternalRequestsBlocked,
+    ComputeIFrameLoopbackIPAddressSpace) {
   {
     IPAddressSpaceCollector collector(shell()->web_contents());
     base::string16 expected_title(base::UTF8ToUTF16("LOADED"));
diff --git a/content/browser/frame_host/sec_fetch_browsertest.cc b/content/browser/frame_host/sec_fetch_browsertest.cc
index fc809563..976fae5 100644
--- a/content/browser/frame_host/sec_fetch_browsertest.cc
+++ b/content/browser/frame_host/sec_fetch_browsertest.cc
@@ -31,18 +31,18 @@
 class SecFetchBrowserTest : public ContentBrowserTest {
  public:
   SecFetchBrowserTest()
-      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
+      : https_test_server_(net::EmbeddedTestServer::TYPE_HTTPS) {
+    feature_list_.InitWithFeatures(
+        {network::features::kFetchMetadata,
+         network::features::kFetchMetadataDestination},
+        {});
+  }
 
   void SetUpOnMainThread() override {
     host_resolver()->AddRule("*", "127.0.0.1");
     https_test_server_.AddDefaultHandlers(GetTestDataFilePath());
     https_test_server_.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
     ASSERT_TRUE(https_test_server_.Start());
-
-    feature_list_.InitWithFeatures(
-        {network::features::kFetchMetadata,
-         network::features::kFetchMetadataDestination},
-        {});
   }
 
   WebContents* web_contents() { return shell()->web_contents(); }
diff --git a/content/browser/native_file_system/file_system_chooser_browsertest.cc b/content/browser/native_file_system/file_system_chooser_browsertest.cc
index e850282..be94ea4 100644
--- a/content/browser/native_file_system/file_system_chooser_browsertest.cc
+++ b/content/browser/native_file_system/file_system_chooser_browsertest.cc
@@ -11,6 +11,7 @@
 #include "content/browser/native_file_system/file_system_chooser_test_helpers.h"
 #include "content/browser/native_file_system/mock_native_file_system_permission_context.h"
 #include "content/browser/native_file_system/native_file_system_manager_impl.h"
+#include "content/browser/web_contents/web_contents_impl.h"
 #include "content/public/browser/browser_context.h"
 #include "content/public/browser/render_process_host.h"
 #include "content/public/browser/storage_partition.h"
@@ -59,6 +60,17 @@
     ui::SelectFileDialog::SetFactory(nullptr);
   }
 
+  bool IsFullscreen() {
+    WebContents* web_contents = shell()->web_contents();
+    return web_contents->IsFullscreenForCurrentTab();
+  }
+
+  void EnterFullscreen(GURL url) {
+    WebContentsImpl* web_contents_impl =
+        static_cast<WebContentsImpl*>(shell()->web_contents());
+    web_contents_impl->EnterFullscreenMode(url, blink::WebFullscreenOptions());
+  }
+
   base::FilePath CreateTestFile(const std::string& contents) {
     base::ScopedAllowBlockingForTesting allow_blocking;
     base::FilePath result;
@@ -114,6 +126,25 @@
              "return await file.text(); })()"));
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, FullscreenOpenFile) {
+  const std::string file_contents = "hello world!";
+  const base::FilePath test_file = CreateTestFile(file_contents);
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_file}, &dialog_params));
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  EnterFullscreen(embedded_test_server()->GetURL("/title1.html"));
+  EXPECT_TRUE(IsFullscreen());
+  EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
+            EvalJs(shell(),
+                   "(async () => {"
+                   "  let e = await self.chooseFileSystemEntries();"
+                   "  self.selected_entry = e;"
+                   "  return e.name; })()"));
+  EXPECT_FALSE(IsFullscreen());
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, SaveFile_NonExistingFile) {
   const std::string file_contents = "file contents to write";
   const base::FilePath test_file = CreateTestFile("");
@@ -204,6 +235,25 @@
   EXPECT_EQ(ui::SelectFileDialog::SELECT_NONE, dialog_params.type);
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, FullscreenSaveFile) {
+  const base::FilePath test_file = CreateTestFile("Hello World");
+
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_file}, &dialog_params));
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  EnterFullscreen(embedded_test_server()->GetURL("/title1.html"));
+  EXPECT_EQ(test_file.BaseName().AsUTF8Unsafe(),
+            EvalJs(shell(),
+                   "(async () => {"
+                   "  let e = await self.chooseFileSystemEntries("
+                   "      {type: 'saveFile'});"
+                   "  self.entry = e;"
+                   "  return e.name; })()"));
+  EXPECT_FALSE(IsFullscreen());
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenMultipleFiles) {
   const base::FilePath test_file1 = CreateTestFile("file1");
   const base::FilePath test_file2 = CreateTestFile("file2");
@@ -222,6 +272,26 @@
   EXPECT_EQ(ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE, dialog_params.type);
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest,
+                       FullscreenOpenMultipleFiles) {
+  const base::FilePath test_file1 = CreateTestFile("file1");
+  const base::FilePath test_file2 = CreateTestFile("file2");
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(new FakeSelectFileDialogFactory(
+      {test_file1, test_file2}, &dialog_params));
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  EnterFullscreen(embedded_test_server()->GetURL("/title1.html"));
+  EXPECT_EQ(ListValueOf(test_file1.BaseName().AsUTF8Unsafe(),
+                        test_file2.BaseName().AsUTF8Unsafe()),
+            EvalJs(shell(),
+                   "(async () => {"
+                   "  let e = await self.chooseFileSystemEntries("
+                   "      {multiple: true});"
+                   "  return e.map(x => x.name); })()"));
+  EXPECT_FALSE(IsFullscreen());
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenDirectory) {
   base::FilePath test_dir = CreateTestDir();
   SelectFileDialogParams dialog_params;
@@ -239,6 +309,24 @@
   EXPECT_EQ(ui::SelectFileDialog::SELECT_FOLDER, dialog_params.type);
 }
 
+IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, FullscreenOpenDirectory) {
+  base::FilePath test_dir = CreateTestDir();
+  SelectFileDialogParams dialog_params;
+  ui::SelectFileDialog::SetFactory(
+      new FakeSelectFileDialogFactory({test_dir}, &dialog_params));
+  ASSERT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/title1.html")));
+  EnterFullscreen(embedded_test_server()->GetURL("/title1.html"));
+  EXPECT_EQ(test_dir.BaseName().AsUTF8Unsafe(),
+            EvalJs(shell(),
+                   "(async () => {"
+                   "  let e = await self.chooseFileSystemEntries("
+                   "      {type: 'openDirectory'});"
+                   "  self.selected_entry = e;"
+                   "  return e.name; })()"));
+  EXPECT_FALSE(IsFullscreen());
+}
+
 IN_PROC_BROWSER_TEST_F(FileSystemChooserBrowserTest, OpenDirectory_DenyAccess) {
   base::FilePath test_dir = CreateTestDir();
   SelectFileDialogParams dialog_params;
diff --git a/content/browser/native_file_system/native_file_system_manager_impl.cc b/content/browser/native_file_system/native_file_system_manager_impl.cc
index 019a7f7f..1aea515 100644
--- a/content/browser/native_file_system/native_file_system_manager_impl.cc
+++ b/content/browser/native_file_system/native_file_system_manager_impl.cc
@@ -92,6 +92,9 @@
     return;
   }
 
+  // Drop fullscreen mode so that the user sees the URL bar.
+  web_contents->ForSecurityDropFullscreen();
+
   FileSystemChooser::CreateAndShow(web_contents, options, std::move(callback),
                                    std::move(callback_runner));
 }
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 8d4158a..1204266 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -217,24 +217,26 @@
 // default server.
 class NavigationBaseBrowserTest : public ContentBrowserTest,
                                   public ::testing::WithParamInterface<bool> {
+ public:
+  NavigationBaseBrowserTest() { ToggleNavigationImmediateResponse(); }
+
  protected:
   void SetUpOnMainThread() override {
-    ToggleNavigationImmediateResponse();
     host_resolver()->AddRule("*", "127.0.0.1");
   }
 
  private:
   void ToggleNavigationImmediateResponse() {
     if (GetParam()) {
-      feature_list.InitAndDisableFeature(
+      feature_list_.InitAndDisableFeature(
           features::kNavigationImmediateResponseBody);
     } else {
-      feature_list.InitAndEnableFeature(
+      feature_list_.InitAndEnableFeature(
           features::kNavigationImmediateResponseBody);
     }
   }
 
-  base::test::ScopedFeatureList feature_list;
+  base::test::ScopedFeatureList feature_list_;
 };
 
 INSTANTIATE_TEST_SUITE_P(/* no prefix */,
@@ -256,8 +258,8 @@
 class NetworkIsolationNavigationBrowserTest
     : public ContentBrowserTest,
       public ::testing::WithParamInterface<bool> {
- protected:
-  void SetUpOnMainThread() override {
+ public:
+  NetworkIsolationNavigationBrowserTest() {
     if (GetParam()) {
       feature_list_.InitAndEnableFeature(
           net::features::kAppendFrameOriginToNetworkIsolationKey);
@@ -265,6 +267,10 @@
       feature_list_.InitAndDisableFeature(
           net::features::kAppendFrameOriginToNetworkIsolationKey);
     }
+  }
+
+ protected:
+  void SetUpOnMainThread() override {
     ASSERT_TRUE(embedded_test_server()->Start());
     ContentBrowserTest::SetUpOnMainThread();
   }
@@ -778,7 +784,16 @@
 
 class NavigationDisableWebSecurityTest : public NavigationBrowserTest {
  public:
-  NavigationDisableWebSecurityTest() {}
+  NavigationDisableWebSecurityTest() {
+    // To get around DataUrlNavigationThrottle. Other attempts at getting around
+    // it don't work, i.e.:
+    // -if the request is made in a child frame then the frame is torn down
+    // immediately on process killing so the navigation doesn't complete
+    // -if it's classified as same document, then a DCHECK in
+    // NavigationRequest::CreateRendererInitiated fires
+    feature_list_.InitAndEnableFeature(
+        features::kAllowContentInitiatedDataUrlNavigations);
+  }
 
  protected:
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -787,6 +802,9 @@
     command_line->AppendSwitch(switches::kDisableWebSecurity);
     NavigationBrowserTest::SetUpCommandLine(command_line);
   }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
 };
 
 // Test to verify that an exploited renderer process trying to specify a
@@ -805,15 +823,6 @@
   base::FilePath file_path = GetTestFilePath("", "simple_page.html");
   GURL file_url = net::FilePathToFileURL(file_path);
 
-  // To get around DataUrlNavigationThrottle. Other attempts at getting around
-  // it don't work, i.e.:
-  // -if the request is made in a child frame then the frame is torn down
-  // immediately on process killing so the navigation doesn't complete
-  // -if it's classified as same document, then a DCHECK in
-  // NavigationRequest::CreateRendererInitiated fires
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      features::kAllowContentInitiatedDataUrlNavigations);
   // Setup a BeginNavigate IPC with non-empty base_url_for_data_url.
   mojom::CommonNavigationParamsPtr common_params =
       mojom::CommonNavigationParams::New(
diff --git a/content/browser/pointer_lock_browsertest.cc b/content/browser/pointer_lock_browsertest.cc
index e576b64..3bc9d0a 100644
--- a/content/browser/pointer_lock_browsertest.cc
+++ b/content/browser/pointer_lock_browsertest.cc
@@ -135,6 +135,16 @@
   MockPointerLockWebContentsDelegate web_contents_delegate_;
 };
 
+class PointerLockBrowserTestWithOptions : public PointerLockBrowserTest {
+ public:
+  PointerLockBrowserTestWithOptions() {
+    feature_list_.InitAndEnableFeature(features::kPointerLockOptions);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, PointerLockBasic) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
@@ -616,10 +626,8 @@
   EXPECT_EQ(nullptr, web_contents()->GetMouseLockWidget());
 }
 
-IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest,
+IN_PROC_BROWSER_TEST_F(PointerLockBrowserTestWithOptions,
                        PointerLockRequestUnadjustedMovement) {
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_feature_list_.InitAndEnableFeature(features::kPointerLockOptions);
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
@@ -667,9 +675,7 @@
 }
 
 #if defined(USE_AURA)
-IN_PROC_BROWSER_TEST_F(PointerLockBrowserTest, UnadjustedMovement) {
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_feature_list_.InitAndEnableFeature(features::kPointerLockOptions);
+IN_PROC_BROWSER_TEST_F(PointerLockBrowserTestWithOptions, UnadjustedMovement) {
   GURL main_url(embedded_test_server()->GetURL(
       "a.com", "/cross_site_iframe_factory.html?a(b)"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
diff --git a/content/browser/renderer_host/input/composited_scrolling_browsertest.cc b/content/browser/renderer_host/input/composited_scrolling_browsertest.cc
index ab9a8907..99c2be1 100644
--- a/content/browser/renderer_host/input/composited_scrolling_browsertest.cc
+++ b/content/browser/renderer_host/input/composited_scrolling_browsertest.cc
@@ -62,8 +62,13 @@
 
 class CompositedScrollingBrowserTest : public ContentBrowserTest {
  public:
-  CompositedScrollingBrowserTest() {}
-  ~CompositedScrollingBrowserTest() override {}
+  CompositedScrollingBrowserTest() {
+    // Disable scroll resampling because this is checking scroll distance.
+    scoped_feature_list_.InitAndDisableFeature(
+        features::kResamplingScrollEvents);
+  }
+
+  ~CompositedScrollingBrowserTest() override = default;
 
   void SetUpCommandLine(base::CommandLine* cmd) override {
     cmd->AppendSwitch(switches::kEnablePreferCompositingToLCDText);
@@ -149,6 +154,7 @@
   }
 
  private:
+  base::test::ScopedFeatureList scoped_feature_list_;
   scoped_refptr<MessageLoopRunner> runner_;
 
   DISALLOW_COPY_AND_ASSIGN(CompositedScrollingBrowserTest);
@@ -166,9 +172,6 @@
 #endif
 IN_PROC_BROWSER_TEST_F(CompositedScrollingBrowserTest,
                        MAYBE_Scroll3DTransformedScroller) {
-  // Disable scroll resampling because this is checking scroll distance.
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_feature_list_.InitAndDisableFeature(features::kResamplingScrollEvents);
   LoadURL();
   double scroll_distance =
       DoTouchScroll(gfx::Point(50, 150), gfx::Vector2d(0, 100));
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index c9a43d78..4d5b42f 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -681,63 +681,6 @@
   DISALLOW_COPY_AND_ASSIGN(SpareRenderProcessHostManager);
 };
 
-const void* const kDefaultSubframeProcessHostHolderKey =
-    &kDefaultSubframeProcessHostHolderKey;
-
-class DefaultSubframeProcessHostHolder : public base::SupportsUserData::Data,
-                                         public RenderProcessHostObserver {
- public:
-  explicit DefaultSubframeProcessHostHolder(BrowserContext* browser_context)
-      : browser_context_(browser_context) {}
-  ~DefaultSubframeProcessHostHolder() override {}
-
-  // Gets the correct render process to use for this SiteInstance.
-  RenderProcessHost* GetProcessHost(SiteInstance* site_instance,
-                                    bool is_for_guests_only) {
-    StoragePartitionImpl* default_partition =
-        static_cast<StoragePartitionImpl*>(
-            BrowserContext::GetDefaultStoragePartition(browser_context_));
-    StoragePartitionImpl* partition = static_cast<StoragePartitionImpl*>(
-        BrowserContext::GetStoragePartition(browser_context_, site_instance));
-
-    // Is this the default storage partition? If it isn't, then just give it its
-    // own non-shared process.
-    if (partition != default_partition || is_for_guests_only) {
-      RenderProcessHost* host = RenderProcessHostImpl::CreateRenderProcessHost(
-          browser_context_, partition, site_instance, is_for_guests_only);
-      host->SetIsNeverSuitableForReuse();
-      return host;
-    }
-
-    // If we already have a shared host for the default storage partition, use
-    // it.
-    if (host_)
-      return host_;
-
-    host_ = RenderProcessHostImpl::CreateRenderProcessHost(
-        browser_context_, partition, site_instance,
-        false /* is for guests only */);
-    host_->SetIsNeverSuitableForReuse();
-    host_->AddObserver(this);
-
-    return host_;
-  }
-
-  // Implementation of RenderProcessHostObserver.
-  void RenderProcessHostDestroyed(RenderProcessHost* host) override {
-    DCHECK_EQ(host_, host);
-    host_->RemoveObserver(this);
-    host_ = nullptr;
-  }
-
- private:
-  BrowserContext* browser_context_;
-
-  // The default subframe render process used for the default storage partition
-  // of this BrowserContext.
-  RenderProcessHost* host_ = nullptr;
-};
-
 // Forwards service requests to Service Manager since the renderer cannot launch
 // out-of-process services on is own.
 template <typename Interface>
@@ -2604,14 +2547,7 @@
   }
 }
 
-void RenderProcessHostImpl::SetIsNeverSuitableForReuse() {
-  is_never_suitable_for_reuse_ = true;
-}
-
 bool RenderProcessHostImpl::MayReuseHost() {
-  if (is_never_suitable_for_reuse_)
-    return false;
-
   return GetContentClient()->browser()->MayReuseHost(this);
 }
 
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 0e3484f1..3418a7d6 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -238,7 +238,6 @@
       mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
       override;
 
-  void SetIsNeverSuitableForReuse() override;
   bool MayReuseHost() override;
   bool IsUnused() override;
   void SetIsUsed() override;
@@ -809,10 +808,6 @@
   // no longer be modified.
   bool is_keep_alive_ref_count_disabled_;
 
-  // Whether this host is never suitable for reuse as determined in the
-  // MayReuseHost() function.
-  bool is_never_suitable_for_reuse_ = false;
-
   // The registered IPC listener objects. When this list is empty, we should
   // delete ourselves.
   base::IDMap<IPC::Listener*> listeners_;
diff --git a/content/browser/renderer_host/render_process_host_unittest.cc b/content/browser/renderer_host/render_process_host_unittest.cc
index 42aaf77..277ae4a 100644
--- a/content/browser/renderer_host/render_process_host_unittest.cc
+++ b/content/browser/renderer_host/render_process_host_unittest.cc
@@ -407,18 +407,6 @@
   EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess());
 }
 
-// Tests that RenderProcessHost will not consider reusing a process that is
-// marked as never suitable for reuse, according to MayReuseHost().
-TEST_F(RenderProcessHostUnitTest, DoNotReuseHostThatIsNeverSuitableForReuse) {
-  const GURL kUrl("http://foo.com");
-  NavigateAndCommit(kUrl);
-  main_test_rfh()->GetProcess()->SetIsNeverSuitableForReuse();
-  scoped_refptr<SiteInstanceImpl> site_instance =
-      SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(),
-                                                         kUrl);
-  EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess());
-}
-
 // Tests that RenderProcessHost reuse considers navigations correctly.
 // Disabled for flakiness: see https://crbug.com/826595
 TEST_F(RenderProcessHostUnitTest, DISABLED_ReuseNavigationProcess) {
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index dc28d77..f8c60b4 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -589,7 +589,10 @@
 
 class CorsExploitBrowserTest : public ContentBrowserTest {
  public:
-  CorsExploitBrowserTest() = default;
+  CorsExploitBrowserTest() {
+    feature_list_.InitAndEnableFeature(
+        blink::features::kHtmlImportsRequestInitiatorLock);
+  }
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
     // TODO(yoichio): This is temporary switch to support chrome internal
@@ -605,6 +608,8 @@
   }
 
  private:
+  base::test::ScopedFeatureList feature_list_;
+
   DISALLOW_COPY_AND_ASSIGN(CorsExploitBrowserTest);
 };
 
@@ -618,9 +623,6 @@
 // deprecated.
 IN_PROC_BROWSER_TEST_F(CorsExploitBrowserTest,
                        OriginHeaderSpoofViaHtmlImports) {
-  base::test::ScopedFeatureList feature_list;
-  feature_list.InitAndEnableFeature(
-      blink::features::kHtmlImportsRequestInitiatorLock);
   std::string victim_path = "/victim/secret.json";
   net::test_server::ControllableHttpResponse victim_response(
       embedded_test_server(), victim_path, false);
diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc
index 1a7d60c..5e0a898 100644
--- a/content/browser/service_worker/service_worker_job_unittest.cc
+++ b/content/browser/service_worker/service_worker_job_unittest.cc
@@ -122,6 +122,31 @@
   (*host)->RequestTermination(base::DoNothing());
 }
 
+class EmbeddedWorkerStatusObserver : public ServiceWorkerVersion::Observer {
+ public:
+  EmbeddedWorkerStatusObserver(base::OnceClosure quit_closure,
+                               EmbeddedWorkerStatus running_status)
+      : quit_closure_(std::move(quit_closure)),
+        expected_running_status_(running_status) {}
+
+  void OnRunningStateChanged(ServiceWorkerVersion* version) override {
+    if (!quit_closure_)
+      return;
+
+    if (version->running_status() == expected_running_status_)
+      std::move(quit_closure_).Run();
+  }
+
+ private:
+  EmbeddedWorkerStatusObserver(const EmbeddedWorkerStatusObserver&) = delete;
+  EmbeddedWorkerStatusObserver& operator=(const EmbeddedWorkerStatusObserver&) =
+      delete;
+
+  base::OnceClosure quit_closure_;
+
+  EmbeddedWorkerStatus expected_running_status_;
+};
+
 }  // namespace
 
 class ServiceWorkerJobTest : public testing::Test {
@@ -152,6 +177,8 @@
   void RunUnregisterJob(const GURL& scope,
                         blink::ServiceWorkerStatusCode expected_status =
                             blink::ServiceWorkerStatusCode::kOk);
+  void WaitForVersionRunningStatus(scoped_refptr<ServiceWorkerVersion> version,
+                                   EmbeddedWorkerStatus running_status);
   scoped_refptr<ServiceWorkerRegistration> FindRegistrationForScope(
       const GURL& scope,
       blink::ServiceWorkerStatusCode expected_status =
@@ -185,6 +212,19 @@
   run_loop.Run();
 }
 
+void ServiceWorkerJobTest::WaitForVersionRunningStatus(
+    scoped_refptr<ServiceWorkerVersion> version,
+    EmbeddedWorkerStatus running_status) {
+  if (version->running_status() == running_status)
+    return;
+
+  base::RunLoop run_loop;
+  EmbeddedWorkerStatusObserver observer(run_loop.QuitClosure(), running_status);
+  version->AddObserver(&observer);
+  run_loop.Run();
+  version->RemoveObserver(&observer);
+}
+
 scoped_refptr<ServiceWorkerRegistration>
 ServiceWorkerJobTest::FindRegistrationForScope(
     const GURL& scope,
@@ -325,8 +365,11 @@
           helper_.get());
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), options);
-  // Wait until the worker becomes active.
-  base::RunLoop().RunUntilIdle();
+  auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  registration->SetTaskRunnerForTest(runner);
+  TestServiceWorkerObserver observer(helper_->context_wrapper());
+  observer.RunUntilActivated(registration->installing_version(), runner);
+  scoped_refptr<ServiceWorkerVersion> version = registration->active_version();
 
   EXPECT_TRUE(registration);
   ASSERT_EQ(2u, worker->events().size());
@@ -340,8 +383,10 @@
   options.scope = GURL("https://www.example.com/");
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), options);
-  // Wait until the worker becomes active.
-  base::RunLoop().RunUntilIdle();
+  auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  registration->SetTaskRunnerForTest(runner);
+  TestServiceWorkerObserver observer(helper_->context_wrapper());
+  observer.RunUntilActivated(registration->installing_version(), runner);
   scoped_refptr<ServiceWorkerVersion> version = registration->active_version();
 
   ServiceWorkerProviderHost* provider_host =
@@ -355,8 +400,8 @@
   EXPECT_EQ(1UL, provider_host->service_worker_object_hosts_.size());
 
   RunUnregisterJob(options.scope);
-  // Wait until the worker becomes redundant.
-  base::RunLoop().RunUntilIdle();
+
+  WaitForVersionRunningStatus(version, EmbeddedWorkerStatus::STOPPED);
 
   // The service worker registration object host and service worker object host
   // have been destroyed together with |provider_host| by the above
@@ -384,11 +429,12 @@
 TEST_F(ServiceWorkerJobTest, RegisterNewScript) {
   blink::mojom::ServiceWorkerRegistrationOptions options;
   options.scope = GURL("https://www.example.com/");
-
   scoped_refptr<ServiceWorkerRegistration> old_registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), options);
-  // Wait until the worker becomes active.
-  base::RunLoop().RunUntilIdle();
+  auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  old_registration->SetTaskRunnerForTest(runner);
+  TestServiceWorkerObserver observer(helper_->context_wrapper());
+  observer.RunUntilActivated(old_registration->installing_version(), runner);
 
   scoped_refptr<ServiceWorkerRegistration> old_registration_by_scope =
       FindRegistrationForScope(options.scope);
@@ -708,8 +754,7 @@
   EXPECT_EQ(ServiceWorkerVersion::INSTALLED, version->status());
 
   RunUnregisterJob(GURL("https://www.example.com/"));
-  // Wait until the worker becomes redundant.
-  base::RunLoop().RunUntilIdle();
+  WaitForVersionRunningStatus(version, EmbeddedWorkerStatus::STOPPED);
 
   // The version should be stopped since there is no controllee after
   // unregistration.
@@ -724,17 +769,20 @@
   options.scope = GURL("https://www.example.com/");
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), options);
-  // Wait until the worker becomes active.
-  base::RunLoop().RunUntilIdle();
+  auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  registration->SetTaskRunnerForTest(runner);
+  TestServiceWorkerObserver observer(helper_->context_wrapper());
+  observer.RunUntilActivated(registration->installing_version(), runner);
   ASSERT_TRUE(registration.get());
 
   scoped_refptr<ServiceWorkerVersion> version = registration->active_version();
+  observer.RunUntilStatusChange(version.get(), ServiceWorkerVersion::ACTIVATED);
   EXPECT_EQ(EmbeddedWorkerStatus::RUNNING, version->running_status());
   EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, version->status());
 
   RunUnregisterJob(GURL("https://www.example.com/"));
-  // Wait until the worker becomes redundant.
-  base::RunLoop().RunUntilIdle();
+
+  WaitForVersionRunningStatus(version, EmbeddedWorkerStatus::STOPPED);
 
   // The version should be stopped since there is no controllee after
   // unregistration.
@@ -750,8 +798,10 @@
   options.scope = GURL("https://www.example.com/");
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), options);
-  // Wait until the worker becomes active.
-  base::RunLoop().RunUntilIdle();
+  auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  registration->SetTaskRunnerForTest(runner);
+  TestServiceWorkerObserver observer(helper_->context_wrapper());
+  observer.RunUntilActivated(registration->installing_version(), runner);
   ASSERT_TRUE(registration.get());
 
   ServiceWorkerProviderHost* host = CreateControllee();
@@ -768,8 +818,7 @@
   EXPECT_EQ(ServiceWorkerVersion::ACTIVATED, version->status());
 
   registration->active_version()->RemoveControllee(host->client_uuid());
-  // Wait until the worker becomes redundant.
-  base::RunLoop().RunUntilIdle();
+  WaitForVersionRunningStatus(version, EmbeddedWorkerStatus::STOPPED);
 
   // The version should be stopped since there is no controllee.
   EXPECT_EQ(EmbeddedWorkerStatus::STOPPED, version->running_status());
@@ -876,8 +925,9 @@
   EXPECT_EQ(ServiceWorkerVersion::INSTALLED, new_version->status());
 
   old_version->RemoveControllee(host->client_uuid());
-  // Wait until the worker becomes redundant.
-  base::RunLoop().RunUntilIdle();
+
+  WaitForVersionRunningStatus(old_version, EmbeddedWorkerStatus::STOPPED);
+  WaitForVersionRunningStatus(new_version, EmbeddedWorkerStatus::STOPPED);
 
   EXPECT_FALSE(registration->is_uninstalling());
   EXPECT_TRUE(registration->is_uninstalled());
@@ -1803,8 +1853,10 @@
   options.scope = GURL("https://www.example.com/one/");
   scoped_refptr<ServiceWorkerRegistration> registration = RunRegisterJob(
       GURL("https://www.example.com/service_worker.js"), options);
-  // Wait until the worker becomes active.
-  base::RunLoop().RunUntilIdle();
+  auto runner = base::MakeRefCounted<base::TestSimpleTaskRunner>();
+  registration->SetTaskRunnerForTest(runner);
+  TestServiceWorkerObserver observer(helper_->context_wrapper());
+  observer.RunUntilActivated(registration->installing_version(), runner);
 
   // Add a controllee and queue an unregister to force the uninstalling state.
   ServiceWorkerProviderHost* host = CreateControllee();
@@ -1865,9 +1917,9 @@
   EXPECT_TRUE(registration->is_uninstalling());
 
   EXPECT_EQ(registration, RunRegisterJob(script3, options));
-  // Wait until the worker becomes redundant.
-  base::RunLoop().RunUntilIdle();
-
+  TestServiceWorkerObserver observer(helper_->context_wrapper());
+  observer.RunUntilStatusChange(second_version.get(),
+                                ServiceWorkerVersion::REDUNDANT);
   scoped_refptr<ServiceWorkerVersion> third_version =
       registration->waiting_version();
   ASSERT_TRUE(third_version);
@@ -1879,7 +1931,6 @@
   RequestTermination(&initial_client->host());
 
   // Wait for activated.
-  TestServiceWorkerObserver observer(helper_->context_wrapper());
   observer.RunUntilActivated(third_version.get(), runner);
 
   // Verify the new version is activated.
diff --git a/content/browser/site_per_process_browsertest.cc b/content/browser/site_per_process_browsertest.cc
index e75ac6fa..ceca07c 100644
--- a/content/browser/site_per_process_browsertest.cc
+++ b/content/browser/site_per_process_browsertest.cc
@@ -669,7 +669,13 @@
 // SitePerProcessBrowserTest
 //
 
-SitePerProcessBrowserTest::SitePerProcessBrowserTest() {}
+SitePerProcessBrowserTest::SitePerProcessBrowserTest() {
+#if !defined(OS_ANDROID)
+  // TODO(bokan): Needed for scrollability check in
+  // FrameOwnerPropertiesPropagationScrolling. crbug.com/662196.
+  feature_list_.InitAndDisableFeature(features::kOverlayScrollbar);
+#endif
+}
 
 std::string SitePerProcessBrowserTest::DepictFrameTree(FrameTreeNode* node) {
   return visualizer_.DepictFrameTree(node);
@@ -681,12 +687,6 @@
   IsolateAllSitesForTesting(command_line);
 
   command_line->AppendSwitch(switches::kValidateInputEventStream);
-
-#if !defined(OS_ANDROID)
-  // TODO(bokan): Needed for scrollability check in
-  // FrameOwnerPropertiesPropagationScrolling. crbug.com/662196.
-  feature_list_.InitAndDisableFeature(features::kOverlayScrollbar);
-#endif
 }
 
 void SitePerProcessBrowserTest::SetUpOnMainThread() {
@@ -13360,14 +13360,22 @@
       1);
 }
 
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
-                       ChildFrameCrashMetrics_ScrolledIntoViewAfterTabIsShown) {
-  // Disable the feature to mark hidden tabs with sad frames for reload, since
-  // it makes the scenario for which this test collects metrics impossible.
-  base::test::ScopedFeatureList feature_list_;
-  feature_list_.InitAndDisableFeature(
-      features::kReloadHiddenTabsWithCrashedSubframes);
+class SitePerProcessBrowserTestWithoutSadFrameTabReload
+    : public SitePerProcessBrowserTest {
+ public:
+  SitePerProcessBrowserTestWithoutSadFrameTabReload() {
+    // Disable the feature to mark hidden tabs with sad frames for reload, since
+    // it makes the scenario for which this test collects metrics impossible.
+    feature_list_.InitAndDisableFeature(
+        features::kReloadHiddenTabsWithCrashedSubframes);
+  }
 
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTestWithoutSadFrameTabReload,
+                       ChildFrameCrashMetrics_ScrolledIntoViewAfterTabIsShown) {
   // Start on a page that has a single iframe, which is positioned out of
   // view, and navigate that iframe cross-site.
   GURL main_url(
@@ -13438,6 +13446,20 @@
       1);
 }
 
+class SitePerProcessBrowserTestWithSadFrameTabReload
+    : public SitePerProcessBrowserTest {
+ public:
+  SitePerProcessBrowserTestWithSadFrameTabReload() {
+    // Disable the feature to mark hidden tabs with sad frames for reload, since
+    // it makes the scenario for which this test collects metrics impossible.
+    feature_list_.InitAndEnableFeature(
+        features::kReloadHiddenTabsWithCrashedSubframes);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 // Verify the feature where hidden tabs with crashed subframes are marked for
 // reload.  This avoids showing crashed subframes if a hidden tab is eventually
 // shown.  See https://crbug.com/841572.
@@ -13449,12 +13471,8 @@
 #define MAYBE_ReloadHiddenTabWithCrashedSubframe \
   ReloadHiddenTabWithCrashedSubframe
 #endif
-IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTest,
+IN_PROC_BROWSER_TEST_F(SitePerProcessBrowserTestWithSadFrameTabReload,
                        MAYBE_ReloadHiddenTabWithCrashedSubframe) {
-  base::test::ScopedFeatureList feature_list_;
-  feature_list_.InitAndEnableFeature(
-      features::kReloadHiddenTabsWithCrashedSubframes);
-
   auto crash_process = [](FrameTreeNode* ftn) {
     RenderProcessHost* process = ftn->current_frame_host()->GetProcess();
     RenderProcessHostWatcher crash_observer(
diff --git a/content/browser/site_per_process_hit_test_browsertest.cc b/content/browser/site_per_process_hit_test_browsertest.cc
index 7c79cd8..64c32d8 100644
--- a/content/browser/site_per_process_hit_test_browsertest.cc
+++ b/content/browser/site_per_process_hit_test_browsertest.cc
@@ -6502,6 +6502,18 @@
   }
 }
 
+class SitePerProcessHitTestBrowserTestWithBrowserVerifiedUserActivation
+    : public SitePerProcessHitTestBrowserTest {
+ public:
+  SitePerProcessHitTestBrowserTestWithBrowserVerifiedUserActivation() {
+    feature_list_.InitAndEnableFeature(
+        features::kBrowserVerifiedUserActivation);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
 #if defined(OS_LINUX)
 // Test is flaky. See https://crbug.com/995285
 #define MAYBE_RenderWidgetUserActivationStateTest \
@@ -6510,12 +6522,9 @@
 #define MAYBE_RenderWidgetUserActivationStateTest \
   RenderWidgetUserActivationStateTest
 #endif
-IN_PROC_BROWSER_TEST_P(SitePerProcessHitTestBrowserTest,
-                       MAYBE_RenderWidgetUserActivationStateTest) {
-  base::test::ScopedFeatureList scoped_feature_list_;
-  scoped_feature_list_.InitAndEnableFeature(
-      features::kBrowserVerifiedUserActivation);
-
+IN_PROC_BROWSER_TEST_P(
+    SitePerProcessHitTestBrowserTestWithBrowserVerifiedUserActivation,
+    MAYBE_RenderWidgetUserActivationStateTest) {
   GURL main_url(embedded_test_server()->GetURL(
       "foo.com", "/frame_tree/page_with_positioned_frame.html"));
   EXPECT_TRUE(NavigateToURL(shell(), main_url));
diff --git a/content/browser/web_package/signed_exchange_loader.cc b/content/browser/web_package/signed_exchange_loader.cc
index 78798b3c..5d341d7 100644
--- a/content/browser/web_package/signed_exchange_loader.cc
+++ b/content/browser/web_package/signed_exchange_loader.cc
@@ -11,8 +11,6 @@
 #include "base/feature_list.h"
 #include "base/metrics/histogram_macros.h"
 #include "base/strings/stringprintf.h"
-#include "content/browser/loader/data_pipe_to_source_stream.h"
-#include "content/browser/loader/source_stream_to_data_pipe.h"
 #include "content/browser/web_package/signed_exchange_cert_fetcher_factory.h"
 #include "content/browser/web_package/signed_exchange_devtools_proxy.h"
 #include "content/browser/web_package/signed_exchange_handler.h"
@@ -28,8 +26,10 @@
 #include "net/http/http_util.h"
 #include "net/url_request/redirect_util.h"
 #include "services/network/public/cpp/constants.h"
+#include "services/network/public/cpp/data_pipe_to_source_stream.h"
 #include "services/network/public/cpp/features.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "services/network/public/cpp/source_stream_to_data_pipe.h"
 #include "services/network/public/cpp/url_loader_completion_status.h"
 #include "services/network/public/mojom/url_loader_factory.mojom.h"
 #include "third_party/blink/public/common/loader/url_loader_throttle.h"
@@ -154,7 +154,8 @@
   if (g_signed_exchange_factory_for_testing_) {
     signed_exchange_handler_ = g_signed_exchange_factory_for_testing_->Create(
         outer_request_.url,
-        std::make_unique<DataPipeToSourceStream>(std::move(response_body)),
+        std::make_unique<network::DataPipeToSourceStream>(
+            std::move(response_body)),
         base::BindOnce(&SignedExchangeLoader::OnHTTPExchangeFound,
                        weak_factory_.GetWeakPtr()),
         std::move(cert_fetcher_factory));
@@ -164,7 +165,8 @@
   signed_exchange_handler_ = std::make_unique<SignedExchangeHandler>(
       IsOriginSecure(outer_request_.url),
       HasNoSniffHeader(outer_response_head_), content_type_,
-      std::make_unique<DataPipeToSourceStream>(std::move(response_body)),
+      std::make_unique<network::DataPipeToSourceStream>(
+          std::move(response_body)),
       base::BindOnce(&SignedExchangeLoader::OnHTTPExchangeFound,
                      weak_factory_.GetWeakPtr()),
       std::move(cert_fetcher_factory), outer_request_.load_flags,
@@ -299,7 +301,7 @@
     return;
   }
   pending_body_consumer_ = std::move(consumer_handle);
-  body_data_pipe_adapter_ = std::make_unique<SourceStreamToDataPipe>(
+  body_data_pipe_adapter_ = std::make_unique<network::SourceStreamToDataPipe>(
       std::move(payload_stream), std::move(producer_handle));
 
   StartReadingBody();
diff --git a/content/browser/web_package/signed_exchange_loader.h b/content/browser/web_package/signed_exchange_loader.h
index 376eb77..e5baafe 100644
--- a/content/browser/web_package/signed_exchange_loader.h
+++ b/content/browser/web_package/signed_exchange_loader.h
@@ -34,6 +34,7 @@
 
 namespace network {
 class SharedURLLoaderFactory;
+class SourceStreamToDataPipe;
 }  // namespace network
 
 namespace content {
@@ -44,7 +45,6 @@
 class SignedExchangePrefetchMetricRecorder;
 class SignedExchangeReporter;
 class SignedExchangeValidityPinger;
-class SourceStreamToDataPipe;
 
 // SignedExchangeLoader handles an origin-signed HTTP exchange response. It is
 // created when a SignedExchangeRequestHandler recieves an origin-signed HTTP
@@ -161,7 +161,7 @@
   network::mojom::URLLoaderClientRequest pending_client_request_;
 
   std::unique_ptr<SignedExchangeHandler> signed_exchange_handler_;
-  std::unique_ptr<SourceStreamToDataPipe> body_data_pipe_adapter_;
+  std::unique_ptr<network::SourceStreamToDataPipe> body_data_pipe_adapter_;
 
   // Kept around until ProceedWithResponse is called.
   mojo::ScopedDataPipeConsumerHandle pending_body_consumer_;
diff --git a/content/common/frame_messages.h b/content/common/frame_messages.h
index ab26613..b5a09976 100644
--- a/content/common/frame_messages.h
+++ b/content/common/frame_messages.h
@@ -518,7 +518,7 @@
   IPC_STRUCT_MEMBER(content::Referrer, referrer)
   IPC_STRUCT_MEMBER(url::Origin, initiator_origin)
   IPC_STRUCT_MEMBER(base::string16, suggested_name)
-  IPC_STRUCT_MEMBER(bool, follow_cross_origin_redirects)
+  IPC_STRUCT_MEMBER(network::mojom::RedirectMode, cross_origin_redirects)
   IPC_STRUCT_MEMBER(mojo::MessagePipeHandle, blob_url_token)
 IPC_STRUCT_END()
 
diff --git a/content/public/browser/render_process_host.h b/content/public/browser/render_process_host.h
index 1ecdeca..812102b6 100644
--- a/content/public/browser/render_process_host.h
+++ b/content/public/browser/render_process_host.h
@@ -446,7 +446,6 @@
 
   // Whether this process is locked out from ever being reused for sites other
   // than the ones it currently has.
-  virtual void SetIsNeverSuitableForReuse() = 0;
   virtual bool MayReuseHost() = 0;
 
   // Indicates whether this RenderProcessHost is "unused".  This starts out as
diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h
index d584847..989b912 100644
--- a/content/public/renderer/render_frame.h
+++ b/content/public/renderer/render_frame.h
@@ -314,6 +314,9 @@
   // independent.
   virtual void ConvertViewportToWindow(blink::WebRect* rect) = 0;
 
+  // Returns the device scale factor of the display the render frame is in.
+  virtual float GetDeviceScaleFactor() = 0;
+
  protected:
   ~RenderFrame() override {}
 
diff --git a/content/public/renderer/render_view.h b/content/public/renderer/render_view.h
index a7cb641f..5b3528dc 100644
--- a/content/public/renderer/render_view.h
+++ b/content/public/renderer/render_view.h
@@ -55,9 +55,6 @@
   // Get the routing ID of the view.
   virtual int GetRoutingID() = 0;
 
-  // Returns the device scale factor of the display the render view is in.
-  virtual float GetDeviceScaleFactor() = 0;
-
   // Returns the page's zoom level for the render view.
   virtual float GetZoomLevel() = 0;
 
diff --git a/content/public/test/mock_render_process_host.cc b/content/public/test/mock_render_process_host.cc
index b3a14cc..1830e32b 100644
--- a/content/public/test/mock_render_process_host.cc
+++ b/content/public/test/mock_render_process_host.cc
@@ -64,7 +64,6 @@
       fast_shutdown_started_(false),
       deletion_callback_called_(false),
       is_for_guests_only_(false),
-      is_never_suitable_for_reuse_(false),
       is_process_backgrounded_(false),
       is_unused_(true),
       keep_alive_ref_count_(0),
@@ -434,12 +433,8 @@
                                   original_factory.Unbind());
 }
 
-void MockRenderProcessHost::SetIsNeverSuitableForReuse() {
-  is_never_suitable_for_reuse_ = true;
-}
-
 bool MockRenderProcessHost::MayReuseHost() {
-  return !is_never_suitable_for_reuse_;
+  return true;
 }
 
 bool MockRenderProcessHost::IsUnused() {
diff --git a/content/public/test/mock_render_process_host.h b/content/public/test/mock_render_process_host.h
index 441744d6..fd4afea 100644
--- a/content/public/test/mock_render_process_host.h
+++ b/content/public/test/mock_render_process_host.h
@@ -162,7 +162,6 @@
       mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver)
       override;
 
-  void SetIsNeverSuitableForReuse() override;
   bool MayReuseHost() override;
   bool IsUnused() override;
   void SetIsUsed() override;
@@ -257,7 +256,6 @@
   bool fast_shutdown_started_;
   bool deletion_callback_called_;
   bool is_for_guests_only_;
-  bool is_never_suitable_for_reuse_;
   bool is_process_backgrounded_;
   bool is_unused_;
   base::Process process;
diff --git a/content/renderer/accessibility/render_accessibility_impl.cc b/content/renderer/accessibility/render_accessibility_impl.cc
index c60bd26..c864d40 100644
--- a/content/renderer/accessibility/render_accessibility_impl.cc
+++ b/content/renderer/accessibility/render_accessibility_impl.cc
@@ -1127,7 +1127,7 @@
 void RenderAccessibilityImpl::RecordImageMetrics(AXContentTreeUpdate* update) {
   if (!render_frame_->accessibility_mode().has_mode(ui::AXMode::kScreenReader))
     return;
-  float scale_factor = render_frame_->GetRenderView()->GetDeviceScaleFactor();
+  float scale_factor = render_frame_->GetDeviceScaleFactor();
   for (size_t i = 0; i < update->nodes.size(); ++i) {
     ui::AXNodeData& node_data = update->nodes[i];
     if (node_data.role != ax::mojom::Role::kImage)
diff --git a/content/renderer/compositor/compositor_dependencies.h b/content/renderer/compositor/compositor_dependencies.h
index a8d3a17..021df9fb 100644
--- a/content/renderer/compositor/compositor_dependencies.h
+++ b/content/renderer/compositor/compositor_dependencies.h
@@ -35,6 +35,7 @@
 
 namespace content {
 class FrameSwapMessageQueue;
+class RenderWidget;
 
 class CONTENT_EXPORT CompositorDependencies {
  public:
@@ -63,7 +64,7 @@
   using LayerTreeFrameSinkCallback =
       base::OnceCallback<void(std::unique_ptr<cc::LayerTreeFrameSink>)>;
   virtual void RequestNewLayerTreeFrameSink(
-      int widget_routing_id,
+      RenderWidget* render_widget,
       scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue,
       const GURL& url,
       LayerTreeFrameSinkCallback callback,
diff --git a/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc b/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc
index 8d98387..d99eadc59 100644
--- a/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc
+++ b/content/renderer/media/webrtc/media_stream_remote_video_source_unittest.cc
@@ -15,7 +15,6 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "media/base/video_frame.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom-shared.h"
@@ -23,6 +22,7 @@
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/public/web/modules/mediastream/mock_media_stream_video_sink.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/webrtc/api/video/color_space.h"
 #include "third_party/webrtc/api/video/i420_buffer.h"
@@ -50,8 +50,8 @@
   MediaStreamRemoteVideoSourceTest()
       : task_environment_(base::test::TaskEnvironment::MainThreadType::UI),
         child_process_(new ChildProcess()),
-        mock_factory_(new MockPeerConnectionDependencyFactory()),
-        webrtc_video_track_(MockWebRtcVideoTrack::Create("test")),
+        mock_factory_(new blink::MockPeerConnectionDependencyFactory()),
+        webrtc_video_track_(blink::MockWebRtcVideoTrack::Create("test")),
         remote_source_(nullptr),
         number_of_successful_track_starts_(0),
         number_of_failed_track_starts_(0) {}
@@ -122,15 +122,16 @@
         base::WaitableEvent::ResetPolicy::MANUAL,
         base::WaitableEvent::InitialState::NOT_SIGNALED);
     mock_factory_->GetWebRtcSignalingThread()->PostTask(
-        FROM_HERE, base::BindOnce(
-                       [](MockWebRtcVideoTrack* video_track,
-                          base::WaitableEvent* waitable_event) {
-                         video_track->SetEnded();
-                         waitable_event->Signal();
-                       },
-                       base::Unretained(static_cast<MockWebRtcVideoTrack*>(
-                           webrtc_video_track_.get())),
-                       base::Unretained(&waitable_event)));
+        FROM_HERE,
+        base::BindOnce(
+            [](blink::MockWebRtcVideoTrack* video_track,
+               base::WaitableEvent* waitable_event) {
+              video_track->SetEnded();
+              waitable_event->Signal();
+            },
+            base::Unretained(static_cast<blink::MockWebRtcVideoTrack*>(
+                webrtc_video_track_.get())),
+            base::Unretained(&waitable_event)));
     waitable_event.Wait();
   }
 
@@ -151,7 +152,7 @@
 
   base::test::TaskEnvironment task_environment_;
   std::unique_ptr<ChildProcess> child_process_;
-  std::unique_ptr<MockPeerConnectionDependencyFactory> mock_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory> mock_factory_;
   scoped_refptr<webrtc::VideoTrackInterface> webrtc_video_track_;
   // |remote_source_| is owned by |webkit_source_|.
   MediaStreamRemoteVideoSourceUnderTest* remote_source_;
diff --git a/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc b/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc
index 2ff62f7..277f223 100644
--- a/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc
+++ b/content/renderer/media/webrtc/media_stream_track_metrics_unittest.cc
@@ -9,9 +9,9 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread.h"
 #include "content/renderer/media/webrtc/media_stream_track_metrics.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/webrtc/api/media_stream_interface.h"
 
 using webrtc::AudioSourceInterface;
@@ -91,7 +91,7 @@
 
   void SetUp() override {
     metrics_.reset(new MockMediaStreamTrackMetrics());
-    stream_ = new rtc::RefCountedObject<MockMediaStream>("stream");
+    stream_ = new rtc::RefCountedObject<blink::MockMediaStream>("stream");
     signaling_thread_.Start();
   }
 
diff --git a/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc b/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc
index 65d4f155..ee46734 100644
--- a/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc
+++ b/content/renderer/media/webrtc/media_stream_video_webrtc_sink_unittest.cc
@@ -6,11 +6,11 @@
 
 #include "base/test/task_environment.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/web/modules/mediastream/mock_media_stream_registry.h"
 #include "third_party/blink/public/web/modules/mediastream/video_track_adapter_settings.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 
 namespace content {
 namespace {
@@ -44,7 +44,7 @@
 
  protected:
   blink::WebMediaStreamTrack track_;
-  MockPeerConnectionDependencyFactory dependency_factory_;
+  blink::MockPeerConnectionDependencyFactory dependency_factory_;
 
  private:
   blink::MockMediaStreamRegistry registry_;
diff --git a/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc b/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc
index 6510346..fd0c0c9 100644
--- a/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc
+++ b/content/renderer/media/webrtc/peer_connection_dependency_factory_unittest.cc
@@ -3,12 +3,12 @@
 // found in the LICENSE file.
 
 #include "base/test/task_environment.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 
 namespace content {
 
@@ -24,7 +24,7 @@
     : public blink::Platform {
  public:
   PeerConnectionDependencyFactoryTestingPlatformSupport(
-      MockPeerConnectionDependencyFactory* dependency_factory)
+      blink::MockPeerConnectionDependencyFactory* dependency_factory)
       : dependency_factory_(dependency_factory) {}
 
   std::unique_ptr<blink::WebRTCPeerConnectionHandler>
@@ -36,13 +36,13 @@
   }
 
  private:
-  MockPeerConnectionDependencyFactory* dependency_factory_ = nullptr;
+  blink::MockPeerConnectionDependencyFactory* dependency_factory_ = nullptr;
 };
 
 class PeerConnectionDependencyFactoryTest : public ::testing::Test {
  public:
   void SetUp() override {
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
 
     platform_original_ = blink::Platform::Current();
     peer_connection_dependency_factory_platform_support_.reset(
@@ -58,7 +58,8 @@
 
  protected:
   base::test::SingleThreadTaskEnvironment task_environment_;
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
 
   std::unique_ptr<PeerConnectionDependencyFactoryTestingPlatformSupport>
       peer_connection_dependency_factory_platform_support_;
diff --git a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
index c6e2b52..7dda097f 100644
--- a/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
+++ b/content/renderer/media/webrtc/peer_connection_tracker_unittest.cc
@@ -9,7 +9,6 @@
 #include "content/common/media/peer_connection_tracker_messages.h"
 #include "content/public/test/mock_render_thread.h"
 #include "content/renderer/media/webrtc/fake_rtc_rtp_transceiver.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h"
 #include "content/renderer/media/webrtc/rtc_peer_connection_handler.h"
 #include "ipc/ipc_message_macros.h"
@@ -22,6 +21,7 @@
 #include "third_party/blink/public/platform/web_rtc_rtp_receiver.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_sender.h"
 #include "third_party/blink/public/platform/web_rtc_rtp_transceiver.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 
 using ::testing::_;
 
@@ -131,7 +131,7 @@
   MOCK_METHOD0(CloseClientPeerConnection, void());
 
  private:
-  MockPeerConnectionDependencyFactory dependency_factory_;
+  blink::MockPeerConnectionDependencyFactory dependency_factory_;
   MockWebRTCPeerConnectionHandlerClient client_;
 };
 
diff --git a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
index 58ccdff..9ec7db587 100644
--- a/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_peer_connection_handler_unittest.cc
@@ -25,9 +25,6 @@
 #include "base/values.h"
 #include "content/child/child_process.h"
 #include "content/renderer/media/audio/mock_audio_device_factory.h"
-#include "content/renderer/media/webrtc/mock_data_channel_impl.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "content/renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h"
 #include "content/renderer/media/webrtc/peer_connection_tracker.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -55,6 +52,9 @@
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/public/web/modules/mediastream/mock_media_stream_video_source.h"
 #include "third_party/blink/public/web/modules/mediastream/processed_local_audio_source.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_data_channel_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/blink/public/web/modules/webrtc/webrtc_audio_device_impl.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
@@ -274,8 +274,8 @@
             dependency_factory,
             blink::scheduler::GetSingleThreadTaskRunnerForTesting()) {}
 
-  MockPeerConnectionImpl* native_peer_connection() {
-    return static_cast<MockPeerConnectionImpl*>(
+  blink::MockPeerConnectionImpl* native_peer_connection() {
+    return static_cast<blink::MockPeerConnectionImpl*>(
         RTCPeerConnectionHandler::native_peer_connection());
   }
 
@@ -290,7 +290,8 @@
 
   void SetUp() override {
     mock_client_.reset(new NiceMock<MockWebRTCPeerConnectionHandlerClient>());
-    mock_dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    mock_dependency_factory_.reset(
+        new blink::MockPeerConnectionDependencyFactory());
 
     platform_original_ = blink::Platform::Current();
     webrtc_audio_device_platform_support_ =
@@ -391,12 +392,12 @@
     rtc::scoped_refptr<webrtc::MediaStreamInterface> stream(
         mock_dependency_factory_->CreateLocalMediaStream(stream_label).get());
     if (!video_track_label.empty()) {
-      InvokeAddTrack(stream,
-                     MockWebRtcVideoTrack::Create(video_track_label).get());
+      InvokeAddTrack(
+          stream, blink::MockWebRtcVideoTrack::Create(video_track_label).get());
     }
     if (!audio_track_label.empty()) {
-      InvokeAddTrack(stream,
-                     MockWebRtcAudioTrack::Create(audio_track_label).get());
+      InvokeAddTrack(
+          stream, blink::MockWebRtcAudioTrack::Create(audio_track_label).get());
     }
     mock_peer_connection_->AddRemoteStream(stream);
     return stream;
@@ -478,7 +479,7 @@
       const rtc::scoped_refptr<webrtc::MediaStreamTrackInterface>& remote_track,
       const rtc::scoped_refptr<webrtc::MediaStreamInterface>& remote_stream) {
     rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver(
-        new rtc::RefCountedObject<FakeRtpReceiver>(remote_track));
+        new rtc::RefCountedObject<blink::FakeRtpReceiver>(remote_track));
     receivers_by_track_.insert(std::make_pair(remote_track.get(), receiver));
     std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>
         receiver_streams;
@@ -588,13 +589,14 @@
       webrtc_audio_device_platform_support_;
   blink::Platform* platform_original_ = nullptr;
   std::unique_ptr<MockWebRTCPeerConnectionHandlerClient> mock_client_;
-  std::unique_ptr<MockPeerConnectionDependencyFactory> mock_dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      mock_dependency_factory_;
   std::unique_ptr<NiceMock<MockPeerConnectionTracker>> mock_tracker_;
   std::unique_ptr<RTCPeerConnectionHandlerUnderTest> pc_handler_;
   MockAudioDeviceFactory mock_audio_device_factory_;
 
   // Weak reference to the mocked native peer connection implementation.
-  MockPeerConnectionImpl* mock_peer_connection_;
+  blink::MockPeerConnectionImpl* mock_peer_connection_;
 
   std::vector<std::unique_ptr<RTCRtpSender>> senders_;
   std::map<webrtc::MediaStreamTrackInterface*,
@@ -642,7 +644,7 @@
   EXPECT_CALL(*mock_client_.get(), DidAddRemoteDataChannel(_)).Times(0);
   webrtc::DataChannelInit config;
   rtc::scoped_refptr<webrtc::DataChannelInterface> remote_data_channel(
-      new rtc::RefCountedObject<MockDataChannel>("dummy", &config));
+      new rtc::RefCountedObject<blink::MockDataChannel>("dummy", &config));
   pc_handler_->observer()->OnDataChannel(remote_data_channel);
 
   RunMessageLoopsUntilIdle();
diff --git a/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc b/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc
index a789dd2a..1d9097d 100644
--- a/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_receiver_unittest.cc
@@ -14,13 +14,13 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/test/task_environment.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "content/renderer/media/webrtc/test/webrtc_stats_report_obtainer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/webrtc/api/stats/rtc_stats_report.h"
@@ -32,11 +32,11 @@
 class RTCRtpReceiverTest : public ::testing::Test {
  public:
   void SetUp() override {
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
     main_thread_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
     track_map_ = new blink::WebRtcMediaStreamTrackAdapterMap(
         dependency_factory_.get(), main_thread_);
-    peer_connection_ = new rtc::RefCountedObject<MockPeerConnectionImpl>(
+    peer_connection_ = new rtc::RefCountedObject<blink::MockPeerConnectionImpl>(
         dependency_factory_.get(), nullptr);
   }
 
@@ -106,17 +106,18 @@
   base::test::TaskEnvironment task_environment_;
   ChildProcess child_process_;
 
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map_;
-  rtc::scoped_refptr<MockPeerConnectionImpl> peer_connection_;
+  rtc::scoped_refptr<blink::MockPeerConnectionImpl> peer_connection_;
   rtc::scoped_refptr<webrtc::MockRtpReceiver> mock_webrtc_receiver_;
   std::unique_ptr<RTCRtpReceiver> receiver_;
 };
 
 TEST_F(RTCRtpReceiverTest, CreateReceiver) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("webrtc_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("webrtc_track");
   receiver_ = CreateReceiver(webrtc_track);
   EXPECT_FALSE(receiver_->Track().IsNull());
   EXPECT_EQ(receiver_->Track().Id().Utf8(), webrtc_track->id());
@@ -124,8 +125,8 @@
 }
 
 TEST_F(RTCRtpReceiverTest, ShallowCopy) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("webrtc_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("webrtc_track");
   receiver_ = CreateReceiver(webrtc_track);
   auto copy = std::make_unique<RTCRtpReceiver>(*receiver_);
   EXPECT_EQ(receiver_->state().track_ref()->webrtc_track(), webrtc_track);
@@ -143,8 +144,8 @@
 }
 
 TEST_F(RTCRtpReceiverTest, GetStats) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("webrtc_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("webrtc_track");
   receiver_ = CreateReceiver(webrtc_track);
 
   // Make the mock return a blink version of the |webtc_report|. The mock does
diff --git a/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc b/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
index 8d646544..6804d36 100644
--- a/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_sender_unittest.cc
@@ -13,8 +13,6 @@
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "content/renderer/media/webrtc/test/webrtc_stats_report_obtainer.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
@@ -24,6 +22,8 @@
 #include "third_party/blink/public/platform/web_rtc_stats.h"
 #include "third_party/blink/public/platform/web_rtc_void_request.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/webrtc/api/stats/rtc_stats_report.h"
@@ -38,11 +38,11 @@
 class RTCRtpSenderTest : public ::testing::Test {
  public:
   void SetUp() override {
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
     main_thread_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
     track_map_ = new blink::WebRtcMediaStreamTrackAdapterMap(
         dependency_factory_.get(), main_thread_);
-    peer_connection_ = new rtc::RefCountedObject<MockPeerConnectionImpl>(
+    peer_connection_ = new rtc::RefCountedObject<blink::MockPeerConnectionImpl>(
         dependency_factory_.get(), nullptr);
     mock_webrtc_sender_ = new rtc::RefCountedObject<webrtc::MockRtpSender>();
   }
@@ -146,10 +146,11 @@
   base::test::TaskEnvironment task_environment_;
   ChildProcess child_process_;
 
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map_;
-  rtc::scoped_refptr<MockPeerConnectionImpl> peer_connection_;
+  rtc::scoped_refptr<blink::MockPeerConnectionImpl> peer_connection_;
   rtc::scoped_refptr<webrtc::MockRtpSender> mock_webrtc_sender_;
   std::unique_ptr<RTCRtpSender> sender_;
 };
diff --git a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc b/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
index 21ce760..0ffc82d 100644
--- a/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
+++ b/content/renderer/media/webrtc/rtc_rtp_transceiver_unittest.cc
@@ -16,14 +16,14 @@
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/webrtc/api/test/mock_rtpreceiver.h"
@@ -34,11 +34,11 @@
 class RTCRtpTransceiverTest : public ::testing::Test {
  public:
   void SetUp() override {
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
     main_task_runner_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
     track_map_ = new blink::WebRtcMediaStreamTrackAdapterMap(
         dependency_factory_.get(), main_task_runner_);
-    peer_connection_ = new rtc::RefCountedObject<MockPeerConnectionImpl>(
+    peer_connection_ = new rtc::RefCountedObject<blink::MockPeerConnectionImpl>(
         dependency_factory_.get(), nullptr);
   }
 
@@ -72,7 +72,7 @@
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
   CreateRemoteTrackAndAdapter(const std::string& id) {
     rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> webrtc_track =
-        MockWebRtcAudioTrack::Create(id).get();
+        blink::MockWebRtcAudioTrack::Create(id).get();
     std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
         track_ref;
     base::RunLoop run_loop;
@@ -87,34 +87,34 @@
     return track_ref;
   }
 
-  rtc::scoped_refptr<FakeRtpSender> CreateWebRtcSender(
+  rtc::scoped_refptr<blink::FakeRtpSender> CreateWebRtcSender(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
       const std::string& stream_id) {
-    return new rtc::RefCountedObject<FakeRtpSender>(
+    return new rtc::RefCountedObject<blink::FakeRtpSender>(
         std::move(track), std::vector<std::string>({stream_id}));
   }
 
-  rtc::scoped_refptr<FakeRtpReceiver> CreateWebRtcReceiver(
+  rtc::scoped_refptr<blink::FakeRtpReceiver> CreateWebRtcReceiver(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
       const std::string& stream_id) {
     rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
-        new rtc::RefCountedObject<MockMediaStream>(stream_id));
-    return new rtc::RefCountedObject<FakeRtpReceiver>(
+        new rtc::RefCountedObject<blink::MockMediaStream>(stream_id));
+    return new rtc::RefCountedObject<blink::FakeRtpReceiver>(
         track.get(),
         std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
             {remote_stream}));
   }
 
-  rtc::scoped_refptr<FakeRtpTransceiver> CreateWebRtcTransceiver(
-      rtc::scoped_refptr<FakeRtpSender> sender,
-      rtc::scoped_refptr<FakeRtpReceiver> receiver,
+  rtc::scoped_refptr<blink::FakeRtpTransceiver> CreateWebRtcTransceiver(
+      rtc::scoped_refptr<blink::FakeRtpSender> sender,
+      rtc::scoped_refptr<blink::FakeRtpReceiver> receiver,
       base::Optional<std::string> mid,
       bool stopped,
       webrtc::RtpTransceiverDirection direction,
       base::Optional<webrtc::RtpTransceiverDirection> current_direction) {
     DCHECK(!sender->track() ||
            sender->track()->kind() == receiver->track()->kind());
-    return new rtc::RefCountedObject<FakeRtpTransceiver>(
+    return new rtc::RefCountedObject<blink::FakeRtpTransceiver>(
         receiver->track()->kind() ==
                 webrtc::MediaStreamTrackInterface::kAudioKind
             ? cricket::MEDIA_TYPE_AUDIO
@@ -180,10 +180,11 @@
   base::test::SingleThreadTaskEnvironment task_environment_;
 
  protected:
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_map_;
-  rtc::scoped_refptr<MockPeerConnectionImpl> peer_connection_;
+  rtc::scoped_refptr<blink::MockPeerConnectionImpl> peer_connection_;
 };
 
 TEST_F(RTCRtpTransceiverTest, InitializeTransceiverState) {
diff --git a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc b/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
index 0fe6c044..07fb7f4 100644
--- a/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
+++ b/content/renderer/media/webrtc/transceiver_state_surfacer_unittest.cc
@@ -14,8 +14,6 @@
 #include "base/synchronization/waitable_event.h"
 #include "base/test/task_environment.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
@@ -23,6 +21,8 @@
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/blink/public/web/web_heap.h"
 
 using testing::AnyNumber;
@@ -42,7 +42,7 @@
 class TransceiverStateSurfacerTest : public ::testing::Test {
  public:
   void SetUp() override {
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
     main_task_runner_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
     track_adapter_map_ = new blink::WebRtcMediaStreamTrackAdapterMap(
         dependency_factory_.get(), main_task_runner_);
@@ -50,8 +50,9 @@
                                                  signaling_task_runner()));
     peer_connection_ = dependency_factory_->CreatePeerConnection(
         webrtc::PeerConnectionInterface::RTCConfiguration(), nullptr, nullptr);
-    EXPECT_CALL(*(static_cast<MockPeerConnectionImpl*>(peer_connection_.get())),
-                GetSctpTransport())
+    EXPECT_CALL(
+        *(static_cast<blink::MockPeerConnectionImpl*>(peer_connection_.get())),
+        GetSctpTransport())
         .Times(AnyNumber());
   }
 
@@ -72,14 +73,14 @@
         CreateBlinkLocalTrack(id));
   }
 
-  rtc::scoped_refptr<FakeRtpTransceiver> CreateWebRtcTransceiver(
+  rtc::scoped_refptr<blink::FakeRtpTransceiver> CreateWebRtcTransceiver(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> local_track,
       const std::string& local_stream_id,
       const std::string& remote_track_id,
       const std::string& remote_stream_id,
       rtc::scoped_refptr<webrtc::DtlsTransportInterface> transport) {
-    rtc::scoped_refptr<FakeRtpTransceiver> transceiver =
-        new rtc::RefCountedObject<FakeRtpTransceiver>(
+    rtc::scoped_refptr<blink::FakeRtpTransceiver> transceiver =
+        new rtc::RefCountedObject<blink::FakeRtpTransceiver>(
             local_track->kind() == webrtc::MediaStreamTrackInterface::kAudioKind
                 ? cricket::MEDIA_TYPE_AUDIO
                 : cricket::MEDIA_TYPE_VIDEO,
@@ -93,21 +94,21 @@
     return transceiver;
   }
 
-  rtc::scoped_refptr<FakeRtpSender> CreateWebRtcSender(
+  rtc::scoped_refptr<blink::FakeRtpSender> CreateWebRtcSender(
       rtc::scoped_refptr<webrtc::MediaStreamTrackInterface> track,
       const std::string& stream_id) {
-    return new rtc::RefCountedObject<FakeRtpSender>(
+    return new rtc::RefCountedObject<blink::FakeRtpSender>(
         std::move(track), std::vector<std::string>({stream_id}));
   }
 
-  rtc::scoped_refptr<FakeRtpReceiver> CreateWebRtcReceiver(
+  rtc::scoped_refptr<blink::FakeRtpReceiver> CreateWebRtcReceiver(
       const std::string& track_id,
       const std::string& stream_id) {
     rtc::scoped_refptr<webrtc::AudioTrackInterface> remote_track =
-        MockWebRtcAudioTrack::Create(track_id).get();
+        blink::MockWebRtcAudioTrack::Create(track_id).get();
     rtc::scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
-        new rtc::RefCountedObject<MockMediaStream>(stream_id));
-    return new rtc::RefCountedObject<FakeRtpReceiver>(
+        new rtc::RefCountedObject<blink::MockMediaStream>(stream_id));
+    return new rtc::RefCountedObject<blink::FakeRtpReceiver>(
         remote_track.get(),
         std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
             {remote_stream}));
@@ -274,7 +275,8 @@
 
  protected:
   scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map_;
   std::unique_ptr<TransceiverStateSurfacer> surfacer_;
@@ -308,7 +310,7 @@
   auto local_track_adapter = CreateLocalTrackAndAdapter("local_track");
   auto webrtc_transceiver = CreateWebRtcTransceiver(
       local_track_adapter->webrtc_track(), "local_stream", "remote_track",
-      "remote_stream", new rtc::RefCountedObject<FakeDtlsTransport>());
+      "remote_stream", new rtc::RefCountedObject<blink::FakeDtlsTransport>());
   auto run_loop = AsyncInitializeSurfacerWithCallback(
       {webrtc_transceiver},
       base::BindOnce(
@@ -369,8 +371,9 @@
       new rtc::RefCountedObject<MockSctpTransport>();
   webrtc::SctpTransportInformation sctp_transport_info(
       webrtc::SctpTransportState::kNew);
-  EXPECT_CALL(*(static_cast<MockPeerConnectionImpl*>(peer_connection_.get())),
-              GetSctpTransport())
+  EXPECT_CALL(
+      *(static_cast<blink::MockPeerConnectionImpl*>(peer_connection_.get())),
+      GetSctpTransport())
       .WillRepeatedly(Return(mock_sctp_transport));
   EXPECT_CALL(*mock_sctp_transport.get(), Information())
       .WillRepeatedly(Return(sctp_transport_info));
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc
index 2656120..b389c3ff 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc
@@ -14,13 +14,13 @@
 #include "base/test/task_environment.h"
 #include "base/test/test_timeouts.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
 #include "third_party/blink/public/platform/web_media_stream_source.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
 #include "third_party/blink/public/platform/web_string.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/blink/public/web/web_heap.h"
 
 namespace content {
@@ -28,7 +28,7 @@
 class WebRtcMediaStreamTrackAdapterMapTest : public ::testing::Test {
  public:
   void SetUp() override {
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
     main_thread_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
     map_ = new blink::WebRtcMediaStreamTrackAdapterMap(
         dependency_factory_.get(), main_thread_);
@@ -116,7 +116,8 @@
   base::test::TaskEnvironment task_environment_;
   ChildProcess child_process_;
 
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> map_;
 };
@@ -151,8 +152,8 @@
 }
 
 TEST_F(WebRtcMediaStreamTrackAdapterMapTest, AddAndRemoveRemoteTrackAdapter) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("remote_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("remote_track");
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
       adapter_ref = GetOrCreateRemoteTrackAdapter(webrtc_track.get());
   EXPECT_TRUE(adapter_ref->is_initialized());
@@ -183,8 +184,8 @@
 
 TEST_F(WebRtcMediaStreamTrackAdapterMapTest,
        InitializeRemoteTrackAdapterExplicitly) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("remote_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("remote_track");
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
       adapter_ref = GetOrCreateRemoteTrackAdapter(webrtc_track.get(), false);
   EXPECT_FALSE(adapter_ref->is_initialized());
@@ -218,8 +219,8 @@
       map_->GetLocalTrackAdapter(local_web_track)->GetAdapterForTesting());
   EXPECT_EQ(1u, map_->GetLocalTrackCount());
 
-  scoped_refptr<MockWebRtcAudioTrack> remote_webrtc_track =
-      MockWebRtcAudioTrack::Create(id);
+  scoped_refptr<blink::MockWebRtcAudioTrack> remote_webrtc_track =
+      blink::MockWebRtcAudioTrack::Create(id);
   std::unique_ptr<blink::WebRtcMediaStreamTrackAdapterMap::AdapterRef>
       remote_adapter = GetOrCreateRemoteTrackAdapter(remote_webrtc_track.get());
   EXPECT_TRUE(remote_adapter->is_initialized());
@@ -247,8 +248,8 @@
 }
 
 TEST_F(WebRtcMediaStreamTrackAdapterMapTest, GetMissingRemoteTrackAdapter) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("missing");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("missing");
   EXPECT_EQ(nullptr, map_->GetRemoteTrackAdapter(webrtc_track.get()));
 }
 
@@ -318,7 +319,7 @@
         track_refs;
     for (size_t i = 0u; i < 5u; ++i) {
       track_refs.push_back(map_->GetOrCreateRemoteTrackAdapter(
-          MockWebRtcAudioTrack::Create("remote_track_id")));
+          blink::MockWebRtcAudioTrack::Create("remote_track_id")));
     }
     main_thread_->PostTask(
         FROM_HERE,
diff --git a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
index 96c4b368..84c9cda7 100644
--- a/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
+++ b/content/renderer/media/webrtc/webrtc_set_description_observer_unittest.cc
@@ -15,12 +15,12 @@
 #include "base/test/task_environment.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/blink/public/web/modules/peerconnection/webrtc_media_stream_track_adapter_map.h"
 #include "third_party/blink/public/web/web_heap.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
@@ -224,7 +224,7 @@
 
   void SetUp() override {
     pc_ = new webrtc::MockPeerConnectionInterface;
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
     main_thread_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
     track_adapter_map_ = new blink::WebRtcMediaStreamTrackAdapterMap(
         dependency_factory_.get(), main_thread_);
@@ -263,8 +263,8 @@
         track_adapter_map_->GetOrCreateLocalTrackAdapter(web_local_track);
     scoped_refptr<webrtc::MediaStreamTrackInterface> local_track =
         local_track_adapter->webrtc_track();
-    rtc::scoped_refptr<FakeRtpSender> sender(
-        new rtc::RefCountedObject<FakeRtpSender>(
+    rtc::scoped_refptr<blink::FakeRtpSender> sender(
+        new rtc::RefCountedObject<blink::FakeRtpSender>(
             local_track.get(), std::vector<std::string>({"local_stream"})));
     // A requirement of WebRtcSet[Local/Remote]DescriptionObserverHandler is
     // that local tracks have existing track adapters when the callback is
@@ -272,17 +272,17 @@
     // Here in testing, we ensure it by adding it to |local_track_adapters_|.
     local_track_adapters_.push_back(std::move(local_track_adapter));
 
-    scoped_refptr<MockWebRtcAudioTrack> remote_track =
-        MockWebRtcAudioTrack::Create("remote_track");
+    scoped_refptr<blink::MockWebRtcAudioTrack> remote_track =
+        blink::MockWebRtcAudioTrack::Create("remote_track");
     scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
-        new rtc::RefCountedObject<MockMediaStream>("remote_stream"));
-    rtc::scoped_refptr<FakeRtpReceiver> receiver(
-        new rtc::RefCountedObject<FakeRtpReceiver>(
+        new rtc::RefCountedObject<blink::MockMediaStream>("remote_stream"));
+    rtc::scoped_refptr<blink::FakeRtpReceiver> receiver(
+        new rtc::RefCountedObject<blink::FakeRtpReceiver>(
             remote_track.get(),
             std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
                 {remote_stream.get()})));
     rtc::scoped_refptr<webrtc::RtpTransceiverInterface> transceiver(
-        new rtc::RefCountedObject<FakeRtpTransceiver>(
+        new rtc::RefCountedObject<blink::FakeRtpTransceiver>(
             cricket::MEDIA_TYPE_AUDIO, sender, receiver, base::nullopt, false,
             webrtc::RtpTransceiverDirection::kSendRecv, base::nullopt));
     transceivers_.push_back(transceiver);
@@ -330,12 +330,12 @@
   void CreateReceivers() {
     ASSERT_EQ(StateSurfacerType::kReceiversOnly, surfacer_type_);
 
-    scoped_refptr<MockWebRtcAudioTrack> remote_track =
-        MockWebRtcAudioTrack::Create("remote_track");
+    scoped_refptr<blink::MockWebRtcAudioTrack> remote_track =
+        blink::MockWebRtcAudioTrack::Create("remote_track");
     scoped_refptr<webrtc::MediaStreamInterface> remote_stream(
-        new rtc::RefCountedObject<MockMediaStream>("remote_stream"));
+        new rtc::RefCountedObject<blink::MockMediaStream>("remote_stream"));
     rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver(
-        new rtc::RefCountedObject<FakeRtpReceiver>(
+        new rtc::RefCountedObject<blink::FakeRtpReceiver>(
             remote_track.get(),
             std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>>(
                 {remote_stream.get()})));
@@ -368,7 +368,8 @@
   ChildProcess child_process_;
 
   scoped_refptr<webrtc::MockPeerConnectionInterface> pc_;
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapterMap> track_adapter_map_;
   scoped_refptr<WebRtcSetDescriptionObserverForTest> observer_;
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc
index 43c64f3..2e3518d 100644
--- a/content/renderer/render_frame_impl.cc
+++ b/content/renderer/render_frame_impl.cc
@@ -4595,7 +4595,7 @@
 
 void RenderFrameImpl::DownloadURL(
     const blink::WebURLRequest& request,
-    CrossOriginRedirects cross_origin_redirect_behavior,
+    network::mojom::RedirectMode cross_origin_redirect_behavior,
     mojo::ScopedMessagePipeHandle blob_url_token) {
   if (ShouldThrottleDownload())
     return;
@@ -4606,8 +4606,7 @@
   params.initiator_origin = request.RequestorOrigin();
   if (request.GetSuggestedFilename().has_value())
     params.suggested_name = request.GetSuggestedFilename()->Utf16();
-  params.follow_cross_origin_redirects =
-      (cross_origin_redirect_behavior == CrossOriginRedirects::kFollow);
+  params.cross_origin_redirects = cross_origin_redirect_behavior;
   params.blob_url_token = blob_url_token.release();
 
   Send(new FrameHostMsg_DownloadUrl(routing_id_, params));
@@ -6468,8 +6467,7 @@
   if (info->navigation_policy == blink::kWebNavigationPolicyDownload) {
     mojo::PendingRemote<blink::mojom::BlobURLToken> blob_url_token =
         CloneBlobURLToken(info->blob_url_token.get());
-    DownloadURL(info->url_request,
-                blink::WebLocalFrameClient::CrossOriginRedirects::kFollow,
+    DownloadURL(info->url_request, network::mojom::RedirectMode::kFollow,
                 blob_url_token.PassPipe());
   } else {
     OpenURL(std::move(info));
@@ -7607,4 +7605,9 @@
   GetLocalRootRenderWidget()->ConvertViewportToWindow(rect);
 }
 
+float RenderFrameImpl::GetDeviceScaleFactor() {
+  // TODO(danakj): Get this from the RenderWidget.
+  return render_view_->page_properties()->GetDeviceScaleFactor();
+}
+
 }  // namespace content
diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h
index c660ae4..22e23940 100644
--- a/content/renderer/render_frame_impl.h
+++ b/content/renderer/render_frame_impl.h
@@ -508,6 +508,7 @@
   void SetAllowsCrossBrowsingInstanceFrameLookup() override;
   gfx::RectF ElementBoundsInWindow(const blink::WebElement& element) override;
   void ConvertViewportToWindow(blink::WebRect* rect) override;
+  float GetDeviceScaleFactor() override;
 
   // blink::mojom::AutoplayConfigurationClient implementation:
   void AddAutoplayFlags(const url::Origin& origin,
@@ -732,7 +733,7 @@
                               unsigned source_line,
                               const blink::WebString& stack_trace) override;
   void DownloadURL(const blink::WebURLRequest& request,
-                   CrossOriginRedirects cross_origin_redirect_behavior,
+                   network::mojom::RedirectMode cross_origin_redirect_behavior,
                    mojo::ScopedMessagePipeHandle blob_url_token) override;
   void BeginNavigation(std::unique_ptr<blink::WebNavigationInfo> info) override;
   void WillSendSubmitEvent(const blink::WebFormElement& form) override;
diff --git a/content/renderer/render_frame_impl_browsertest.cc b/content/renderer/render_frame_impl_browsertest.cc
index e4a9a59..9e174e4 100644
--- a/content/renderer/render_frame_impl_browsertest.cc
+++ b/content/renderer/render_frame_impl_browsertest.cc
@@ -358,9 +358,8 @@
       blink::WebSecurityOrigin::Create(GURL("http://test")));
 
   for (int i = 0; i < 10; ++i) {
-    frame()->DownloadURL(
-        request, blink::WebLocalFrameClient::CrossOriginRedirects::kNavigate,
-        mojo::ScopedMessagePipeHandle());
+    frame()->DownloadURL(request, network::mojom::RedirectMode::kManual,
+                         mojo::ScopedMessagePipeHandle());
     base::RunLoop().RunUntilIdle();
     const IPC::Message* msg2 = render_thread_->sink().GetFirstMessageMatching(
         FrameHostMsg_DownloadUrl::ID);
@@ -369,9 +368,8 @@
     render_thread_->sink().ClearMessages();
   }
 
-  frame()->DownloadURL(
-      request, blink::WebLocalFrameClient::CrossOriginRedirects::kNavigate,
-      mojo::ScopedMessagePipeHandle());
+  frame()->DownloadURL(request, network::mojom::RedirectMode::kManual,
+                       mojo::ScopedMessagePipeHandle());
   base::RunLoop().RunUntilIdle();
   const IPC::Message* msg3 = render_thread_->sink().GetFirstMessageMatching(
       FrameHostMsg_DownloadUrl::ID);
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 0a55d4f0..e2cb076 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -1857,7 +1857,7 @@
 }
 
 void RenderThreadImpl::RequestNewLayerTreeFrameSink(
-    int widget_routing_id,
+    RenderWidget* render_widget,
     scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue,
     const GURL& url,
     LayerTreeFrameSinkCallback callback,
@@ -1914,10 +1914,10 @@
   if (is_gpu_compositing_disabled_) {
     DCHECK(!web_test_mode());
     frame_sink_provider_->CreateForWidget(
-        widget_routing_id, std::move(compositor_frame_sink_receiver),
+        render_widget->routing_id(), std::move(compositor_frame_sink_receiver),
         std::move(compositor_frame_sink_client));
     frame_sink_provider_->RegisterRenderFrameMetadataObserver(
-        widget_routing_id,
+        render_widget->routing_id(),
         std::move(render_frame_metadata_observer_client_receiver),
         std::move(render_frame_metadata_observer_remote));
     std::move(callback).Run(
@@ -1976,34 +1976,29 @@
 
 #if defined(OS_ANDROID)
   if (GetContentClient()->UsingSynchronousCompositing()) {
-    RenderWidget* widget = RenderWidget::FromRoutingID(widget_routing_id);
-    if (widget) {
-      // TODO(ericrk): Collapse with non-webview registration below.
-      frame_sink_provider_->RegisterRenderFrameMetadataObserver(
-          widget_routing_id,
-          std::move(render_frame_metadata_observer_client_receiver),
-          std::move(render_frame_metadata_observer_remote));
+    // TODO(ericrk): Collapse with non-webview registration below.
+    frame_sink_provider_->RegisterRenderFrameMetadataObserver(
+        render_widget->routing_id(),
+        std::move(render_frame_metadata_observer_client_receiver),
+        std::move(render_frame_metadata_observer_remote));
 
-      std::move(callback).Run(std::make_unique<SynchronousLayerTreeFrameSink>(
-          std::move(context_provider), std::move(worker_context_provider),
-          compositor_task_runner_, GetGpuMemoryBufferManager(),
-          sync_message_filter(), widget_routing_id,
-          g_next_layer_tree_frame_sink_id++,
-          std::move(params.synthetic_begin_frame_source),
-          widget->widget_input_handler_manager()
-              ->GetSynchronousCompositorRegistry(),
-          std::move(frame_swap_message_queue)));
-      return;
-    } else {
-      NOTREACHED();
-    }
+    std::move(callback).Run(std::make_unique<SynchronousLayerTreeFrameSink>(
+        std::move(context_provider), std::move(worker_context_provider),
+        compositor_task_runner_, GetGpuMemoryBufferManager(),
+        sync_message_filter(), render_widget->routing_id(),
+        g_next_layer_tree_frame_sink_id++,
+        std::move(params.synthetic_begin_frame_source),
+        render_widget->widget_input_handler_manager()
+            ->GetSynchronousCompositorRegistry(),
+        std::move(frame_swap_message_queue)));
+    return;
   }
 #endif
   frame_sink_provider_->CreateForWidget(
-      widget_routing_id, std::move(compositor_frame_sink_receiver),
+      render_widget->routing_id(), std::move(compositor_frame_sink_receiver),
       std::move(compositor_frame_sink_client));
   frame_sink_provider_->RegisterRenderFrameMetadataObserver(
-      widget_routing_id,
+      render_widget->routing_id(),
       std::move(render_frame_metadata_observer_client_receiver),
       std::move(render_frame_metadata_observer_remote));
   params.gpu_memory_buffer_manager = GetGpuMemoryBufferManager();
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index bc7e43d..7dfca92 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -218,7 +218,7 @@
   bool IsScrollAnimatorEnabled() override;
   std::unique_ptr<cc::UkmRecorderFactory> CreateUkmRecorderFactory() override;
   void RequestNewLayerTreeFrameSink(
-      int widget_routing_id,
+      RenderWidget* render_widget,
       scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue,
       const GURL& url,
       LayerTreeFrameSinkCallback callback,
diff --git a/content/renderer/render_view_browsertest.cc b/content/renderer/render_view_browsertest.cc
index dbd2244..32ebc3da 100644
--- a/content/renderer/render_view_browsertest.cc
+++ b/content/renderer/render_view_browsertest.cc
@@ -466,7 +466,7 @@
   void SetDeviceScaleFactor(float dsf) {
     view()->GetWidget()->SynchronizeVisualPropertiesFromRenderView(
         MakeVisualPropertiesWithDeviceScaleFactor(dsf));
-    ASSERT_EQ(dsf, view()->page_properties()->GetDeviceScaleFactor());
+    ASSERT_EQ(dsf, view()->GetMainRenderFrame()->GetDeviceScaleFactor());
     ASSERT_EQ(dsf,
               view()->GetWidget()->GetOriginalScreenInfo().device_scale_factor);
   }
@@ -1010,7 +1010,7 @@
 
   // Verify that the system device scale factor has propagated into the
   // RenderFrameProxy.
-  EXPECT_EQ(device_scale, view()->page_properties()->GetDeviceScaleFactor());
+  EXPECT_EQ(device_scale, view()->GetMainRenderFrame()->GetDeviceScaleFactor());
   EXPECT_EQ(device_scale,
             view()->GetWidget()->GetOriginalScreenInfo().device_scale_factor);
   EXPECT_EQ(device_scale, child_proxy->screen_info().device_scale_factor);
@@ -1018,7 +1018,7 @@
   TestEmulatedSizeDprDsf(640, 480, 3.f, compositor_dsf);
 
   // Verify that the RenderFrameProxy device scale factor is still the same.
-  EXPECT_EQ(3.f, view()->page_properties()->GetDeviceScaleFactor());
+  EXPECT_EQ(3.f, view()->GetMainRenderFrame()->GetDeviceScaleFactor());
   EXPECT_EQ(device_scale,
             view()->GetWidget()->GetOriginalScreenInfo().device_scale_factor);
   EXPECT_EQ(device_scale, child_proxy->screen_info().device_scale_factor);
@@ -1075,7 +1075,7 @@
 TEST_F(RenderViewImplEnableZoomForDSFTest, UpdateDSFAfterSwapIn) {
   const float device_scale = 3.0f;
   SetDeviceScaleFactor(device_scale);
-  EXPECT_EQ(device_scale, view()->GetDeviceScaleFactor());
+  EXPECT_EQ(device_scale, view()->GetMainRenderFrame()->GetDeviceScaleFactor());
 
   LoadHTML("Hello world!");
 
@@ -1135,7 +1135,7 @@
                               CreateCommitNavigationParams());
   base::RunLoop().RunUntilIdle();
 
-  EXPECT_EQ(device_scale, view()->GetDeviceScaleFactor());
+  EXPECT_EQ(device_scale, view()->GetMainRenderFrame()->GetDeviceScaleFactor());
   EXPECT_EQ(device_scale, view()->webview()->ZoomFactorForDeviceScaleFactor());
 
   double device_pixel_ratio;
diff --git a/content/renderer/render_view_impl.cc b/content/renderer/render_view_impl.cc
index 3ab0873c..382d523d 100644
--- a/content/renderer/render_view_impl.cc
+++ b/content/renderer/render_view_impl.cc
@@ -1893,10 +1893,6 @@
   return routing_id_;
 }
 
-float RenderViewImpl::GetDeviceScaleFactor() {
-  return page_properties()->GetDeviceScaleFactor();
-}
-
 float RenderViewImpl::GetZoomLevel() {
   return page_zoom_level_;
 }
@@ -2044,13 +2040,10 @@
 void RenderViewImpl::OnUpdateVisualProperties(
     const VisualProperties& visual_properties,
     int widget_routing_id) {
-  // TODO(https://crbug.com/998273): We should not forward visual properties to
-  // frozen render widgets.
   // The widget may have been destroyed while the IPC was in flight.
   RenderWidget* widget = RenderWidget::FromRoutingID(widget_routing_id);
-  if (widget) {
+  if (widget && !widget->IsUndeadOrProvisional())
     widget->SynchronizeVisualPropertiesFromRenderView(visual_properties);
-  }
 }
 
 void RenderViewImpl::OnUpdatePageVisualProperties(
diff --git a/content/renderer/render_view_impl.h b/content/renderer/render_view_impl.h
index e39f9f3b..2586007 100644
--- a/content/renderer/render_view_impl.h
+++ b/content/renderer/render_view_impl.h
@@ -286,7 +286,6 @@
   bool Send(IPC::Message* message) override;
   RenderFrameImpl* GetMainRenderFrame() override;
   int GetRoutingID() override;
-  float GetDeviceScaleFactor() override;
   float GetZoomLevel() override;
   const WebPreferences& GetWebkitPreferences() override;
   void SetWebkitPreferences(const WebPreferences& preferences) override;
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index a3eda3f..a6c1d3db 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -705,6 +705,12 @@
   TRACE_EVENT0("renderer",
                "RenderWidget::SynchronizeVisualPropertiesFromRenderView");
 
+  // TODO(crbug.com/995981): We shouldn't be sending VisualProperties to undead
+  // RenderWidgets already, but if we do we could crash if the RenderWidget
+  // hasn't been initialized yet. So this acts defensively until we destroy
+  // undead RenderWidgets.
+  DCHECK(!is_undead_);
+
   VisualProperties visual_properties = visual_properties_from_browser;
   // Web tests can override the device scale factor in the renderer.
   if (device_scale_factor_for_testing_) {
@@ -1321,9 +1327,8 @@
   // would also be used for other widgets such as popups.
   const char* client_name = for_child_local_root_frame_ ? kOOPIF : kRenderer;
   compositor_deps_->RequestNewLayerTreeFrameSink(
-      routing_id_, frame_swap_message_queue_, std::move(url),
-      std::move(callback), std::move(client_receiver),
-      std::move(observer_remote), client_name);
+      this, frame_swap_message_queue_, std::move(url), std::move(callback),
+      std::move(client_receiver), std::move(observer_remote), client_name);
 }
 
 void RenderWidget::DidCommitAndDrawCompositorFrame() {
@@ -2003,6 +2008,14 @@
   // compositor since when hidden the compositor is always stopped.
   if (!is_hidden_)
     StartStopCompositor();
+
+  // Remove undead RenderWidgets from the routing map so that they cannot be
+  // looked up with FromRoutingId().
+  if (is_undead) {
+    g_routing_id_widget_map.Get().erase(routing_id_);
+  } else {
+    g_routing_id_widget_map.Get().emplace(routing_id_, this);
+  }
 }
 
 // static
diff --git a/content/shell/browser/shell_browser_main_parts.cc b/content/shell/browser/shell_browser_main_parts.cc
index 09d0eac..d6633e2 100644
--- a/content/shell/browser/shell_browser_main_parts.cc
+++ b/content/shell/browser/shell_browser_main_parts.cc
@@ -83,14 +83,12 @@
       base::MakeAbsoluteFilePath(base::FilePath(args[0])));
 }
 
-base::StringPiece PlatformResourceProvider(int key) {
+scoped_refptr<base::RefCountedMemory> PlatformResourceProvider(int key) {
   if (key == IDR_DIR_HEADER_HTML) {
-    base::StringPiece html_data =
-        ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
-            IDR_DIR_HEADER_HTML);
-    return html_data;
+    return ui::ResourceBundle::GetSharedInstance().LoadDataResourceBytes(
+        IDR_DIR_HEADER_HTML);
   }
-  return base::StringPiece();
+  return nullptr;
 }
 
 }  // namespace
diff --git a/content/shell/renderer/web_test/blink_test_runner.cc b/content/shell/renderer/web_test/blink_test_runner.cc
index c134bba..de5e114 100644
--- a/content/shell/renderer/web_test/blink_test_runner.cc
+++ b/content/shell/renderer/web_test/blink_test_runner.cc
@@ -669,10 +669,6 @@
   return placeholder->plugin();
 }
 
-float BlinkTestRunner::GetDeviceScaleFactor() const {
-  return render_view()->GetDeviceScaleFactor();
-}
-
 void BlinkTestRunner::RunIdleTasks(base::OnceClosure callback) {
   SchedulerRunIdleTasks(std::move(callback));
 }
diff --git a/content/shell/renderer/web_test/blink_test_runner.h b/content/shell/renderer/web_test/blink_test_runner.h
index feeaff1..75697b8 100644
--- a/content/shell/renderer/web_test/blink_test_runner.h
+++ b/content/shell/renderer/web_test/blink_test_runner.h
@@ -136,7 +136,6 @@
   void ResolveBeforeInstallPromptPromise(const std::string& platform) override;
   blink::WebPlugin* CreatePluginPlaceholder(
       const blink::WebPluginParams& params) override;
-  float GetDeviceScaleFactor() const override;
   void RunIdleTasks(base::OnceClosure callback) override;
   void ForceTextInputStateUpdate(blink::WebLocalFrame* frame) override;
   void ExcludeSchemeFromRequestInitiatorSiteLockChecks(
diff --git a/content/shell/test_runner/web_frame_test_client.cc b/content/shell/test_runner/web_frame_test_client.cc
index 3e0015b..f7c0669c 100644
--- a/content/shell/test_runner/web_frame_test_client.cc
+++ b/content/shell/test_runner/web_frame_test_client.cc
@@ -354,8 +354,7 @@
 
 void WebFrameTestClient::DownloadURL(
     const blink::WebURLRequest& request,
-    blink::WebLocalFrameClient::CrossOriginRedirects
-        cross_origin_redirect_behavior,
+    network::mojom::RedirectMode cross_origin_redirect_behavior,
     mojo::ScopedMessagePipeHandle blob_url_token) {
   if (test_runner()->ShouldWaitUntilExternalURLLoad()) {
     delegate_->PrintMessage(std::string("Download started\n"));
diff --git a/content/shell/test_runner/web_frame_test_client.h b/content/shell/test_runner/web_frame_test_client.h
index 4b2adf3..733f36fe 100644
--- a/content/shell/test_runner/web_frame_test_client.h
+++ b/content/shell/test_runner/web_frame_test_client.h
@@ -59,8 +59,7 @@
                               unsigned source_line,
                               const blink::WebString& stack_trace) override;
   void DownloadURL(const blink::WebURLRequest& request,
-                   blink::WebLocalFrameClient::CrossOriginRedirects
-                       cross_origin_redirect_behavior,
+                   network::mojom::RedirectMode cross_origin_redirect_behavior,
                    mojo::ScopedMessagePipeHandle blob_url_token) override;
   void DidReceiveTitle(const blink::WebString& title,
                        blink::WebTextDirection direction) override;
diff --git a/content/shell/test_runner/web_frame_test_proxy.cc b/content/shell/test_runner/web_frame_test_proxy.cc
index 79013b4..9c1b70f 100644
--- a/content/shell/test_runner/web_frame_test_proxy.cc
+++ b/content/shell/test_runner/web_frame_test_proxy.cc
@@ -157,8 +157,7 @@
 
 void WebFrameTestProxy::DownloadURL(
     const blink::WebURLRequest& request,
-    blink::WebLocalFrameClient::CrossOriginRedirects
-        cross_origin_redirect_behavior,
+    network::mojom::RedirectMode cross_origin_redirect_behavior,
     mojo::ScopedMessagePipeHandle blob_url_token) {
   test_client_->DownloadURL(request, cross_origin_redirect_behavior,
                             mojo::ScopedMessagePipeHandle());
diff --git a/content/shell/test_runner/web_frame_test_proxy.h b/content/shell/test_runner/web_frame_test_proxy.h
index 8e867aaa..108481a 100644
--- a/content/shell/test_runner/web_frame_test_proxy.h
+++ b/content/shell/test_runner/web_frame_test_proxy.h
@@ -48,8 +48,7 @@
                               unsigned source_line,
                               const blink::WebString& stack_trace) override;
   void DownloadURL(const blink::WebURLRequest& request,
-                   blink::WebLocalFrameClient::CrossOriginRedirects
-                       cross_origin_redirect_behavior,
+                   network::mojom::RedirectMode cross_origin_redirect_behavior,
                    mojo::ScopedMessagePipeHandle blob_url_token) override;
   void DidReceiveTitle(const blink::WebString& title,
                        blink::WebTextDirection direction) override;
diff --git a/content/shell/test_runner/web_test_delegate.h b/content/shell/test_runner/web_test_delegate.h
index d849432..06d80072 100644
--- a/content/shell/test_runner/web_test_delegate.h
+++ b/content/shell/test_runner/web_test_delegate.h
@@ -247,8 +247,6 @@
   virtual blink::WebPlugin* CreatePluginPlaceholder(
       const blink::WebPluginParams& params) = 0;
 
-  virtual float GetDeviceScaleFactor() const = 0;
-
   // Run all pending idle tasks, and then run callback.
   virtual void RunIdleTasks(base::OnceClosure callback) = 0;
 
diff --git a/content/test/BUILD.gn b/content/test/BUILD.gn
index 4294491a..7026bbd 100644
--- a/content/test/BUILD.gn
+++ b/content/test/BUILD.gn
@@ -233,12 +233,6 @@
     "../public/test/web_contents_binding_set_test_binder.h",
     "../public/test/web_contents_tester.cc",
     "../public/test/web_contents_tester.h",
-    "../renderer/media/webrtc/mock_data_channel_impl.cc",
-    "../renderer/media/webrtc/mock_data_channel_impl.h",
-    "../renderer/media/webrtc/mock_peer_connection_dependency_factory.cc",
-    "../renderer/media/webrtc/mock_peer_connection_dependency_factory.h",
-    "../renderer/media/webrtc/mock_peer_connection_impl.cc",
-    "../renderer/media/webrtc/mock_peer_connection_impl.h",
     "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.cc",
     "../renderer/media/webrtc/mock_web_rtc_peer_connection_handler_client.h",
     "../renderer/media/webrtc/test/webrtc_stats_report_obtainer.cc",
@@ -1680,11 +1674,9 @@
     "../browser/indexed_db/scopes/leveldb_scopes_test_utils.h",
     "../browser/indexed_db/scopes/leveldb_scopes_unittest.cc",
     "../browser/indexed_db/scopes/scopes_lock_manager_unittest.cc",
-    "../browser/loader/data_pipe_to_source_stream_unittest.cc",
     "../browser/loader/merkle_integrity_source_stream_unittest.cc",
     "../browser/loader/navigation_url_loader_impl_unittest.cc",
     "../browser/loader/navigation_url_loader_unittest.cc",
-    "../browser/loader/source_stream_to_data_pipe_unittest.cc",
     "../browser/manifest/manifest_icon_downloader_unittest.cc",
     "../browser/media/audible_metrics_unittest.cc",
     "../browser/media/audio_input_stream_broker_unittest.cc",
@@ -1963,7 +1955,6 @@
     "../renderer/media/webrtc/transceiver_state_surfacer_unittest.cc",
     "../renderer/media/webrtc/webrtc_audio_renderer_unittest.cc",
     "../renderer/media/webrtc/webrtc_media_stream_track_adapter_map_unittest.cc",
-    "../renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc",
     "../renderer/media/webrtc/webrtc_set_description_observer_unittest.cc",
     "../renderer/peripheral_content_heuristic_unittest.cc",
     "../renderer/queue_message_swap_promise_unittest.cc",
@@ -2111,7 +2102,6 @@
     "//third_party/metrics_proto",
     "//third_party/re2",
     "//third_party/sqlite",
-    "//third_party/webrtc/api:libjingle_peerconnection_api",
     "//third_party/webrtc/api:media_stream_interface",
     "//third_party/webrtc/api:rtc_stats_api",
     "//third_party/webrtc/api/task_queue:task_queue_test",
diff --git a/content/test/fake_compositor_dependencies.cc b/content/test/fake_compositor_dependencies.cc
index 16b815c..aa00ba1c 100644
--- a/content/test/fake_compositor_dependencies.cc
+++ b/content/test/fake_compositor_dependencies.cc
@@ -88,7 +88,7 @@
 }
 
 void FakeCompositorDependencies::RequestNewLayerTreeFrameSink(
-    int widget_routing_id,
+    RenderWidget* render_widget,
     scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue,
     const GURL& url,
     LayerTreeFrameSinkCallback callback,
diff --git a/content/test/fake_compositor_dependencies.h b/content/test/fake_compositor_dependencies.h
index f87ffd74..c06d0ab2 100644
--- a/content/test/fake_compositor_dependencies.h
+++ b/content/test/fake_compositor_dependencies.h
@@ -39,7 +39,7 @@
   bool IsScrollAnimatorEnabled() override;
   std::unique_ptr<cc::UkmRecorderFactory> CreateUkmRecorderFactory() override;
   void RequestNewLayerTreeFrameSink(
-      int widget_routing_id,
+      RenderWidget* render_widget,
       scoped_refptr<FrameSwapMessageQueue> frame_swap_message_queue,
       const GURL& url,
       LayerTreeFrameSinkCallback callback,
diff --git a/device/fido/fido_request_handler_base.cc b/device/fido/fido_request_handler_base.cc
index c72e765..3b4664e 100644
--- a/device/fido/fido_request_handler_base.cc
+++ b/device/fido/fido_request_handler_base.cc
@@ -231,13 +231,6 @@
     discovery->Start();
 }
 
-void FidoRequestHandlerBase::AuthenticatorAdded(
-    FidoDiscoveryBase* discovery,
-    FidoAuthenticator* authenticator) {
-  DCHECK(!base::Contains(active_authenticators(), authenticator->GetId()));
-  AddAuthenticator(authenticator);
-}
-
 void FidoRequestHandlerBase::AuthenticatorRemoved(
     FidoDiscoveryBase* discovery,
     FidoAuthenticator* authenticator) {
@@ -288,14 +281,15 @@
     FidoDiscoveryBase* discovery,
     bool success,
     std::vector<FidoAuthenticator*> authenticators) {
-  for (auto* authenticator : authenticators)
-    AddAuthenticator(authenticator);
-
+  for (auto* authenticator : authenticators) {
+    AuthenticatorAdded(discovery, authenticator);
+  }
   DCHECK(notify_observer_callback_);
   notify_observer_callback_.Run();
 }
 
-void FidoRequestHandlerBase::AddAuthenticator(
+void FidoRequestHandlerBase::AuthenticatorAdded(
+    FidoDiscoveryBase* discovery,
     FidoAuthenticator* authenticator) {
   DCHECK(authenticator &&
          !base::Contains(active_authenticators(), authenticator->GetId()));
diff --git a/device/fido/fido_request_handler_base.h b/device/fido/fido_request_handler_base.h
index 005bdd5..9969d07 100644
--- a/device/fido/fido_request_handler_base.h
+++ b/device/fido/fido_request_handler_base.h
@@ -262,7 +262,6 @@
       const base::flat_set<FidoTransportProtocol>& available_transports);
 #endif
 
-  void AddAuthenticator(FidoAuthenticator* authenticator);
   void NotifyObserverTransportAvailability();
 
   // Invokes FidoAuthenticator::InitializeAuthenticator(), followed by
diff --git a/device/fido/get_assertion_request_handler.cc b/device/fido/get_assertion_request_handler.cc
index 5f6f7c4..ba54744 100644
--- a/device/fido/get_assertion_request_handler.cc
+++ b/device/fido/get_assertion_request_handler.cc
@@ -321,6 +321,9 @@
   DCHECK_CALLED_ON_VALID_SEQUENCE(my_sequence_checker_);
 
 #if defined(OS_MACOSX)
+  // Indicate to the UI whether a GetAssertion call to Touch ID would succeed
+  // or not. This needs to happen before the base AuthenticatorAdded()
+  // implementation runs |notify_observer_callback_| for this callback.
   if (authenticator->IsTouchIdAuthenticator()) {
     transport_availability_info().has_recognized_mac_touch_id_credential =
         static_cast<fido::mac::TouchIdAuthenticator*>(authenticator)
diff --git a/device/vr/orientation/orientation_device.cc b/device/vr/orientation/orientation_device.cc
index fe4b2bd..d6e1a986 100644
--- a/device/vr/orientation/orientation_device.cc
+++ b/device/vr/orientation/orientation_device.cc
@@ -150,6 +150,9 @@
   }
 
   std::move(callback).Run(std::move(session), std::move(controller));
+
+  // The sensor may have been suspended, so resume it now.
+  sensor_->Resume();
 }
 
 void VROrientationDevice::EndMagicWindowSession(VROrientationSession* session) {
@@ -157,10 +160,18 @@
                 [session](const std::unique_ptr<VROrientationSession>& item) {
                   return item.get() == session;
                 });
+
+  // If there are no more magic window sessions, suspend the sensor until we get
+  // a new one.
+  if (magic_window_sessions_.empty()) {
+    sensor_->Suspend();
+  }
 }
 
 void VROrientationDevice::GetInlineFrameData(
     mojom::XRFrameDataProvider::GetFrameDataCallback callback) {
+  // Orientation sessions should never be exclusive or presenting.
+  DCHECK(!HasExclusiveSession());
   if (!inline_poses_enabled_) {
     std::move(callback).Run(nullptr);
     return;
diff --git a/device/vr/orientation/orientation_session.cc b/device/vr/orientation/orientation_session.cc
index 05a7ade..a71ae75 100644
--- a/device/vr/orientation/orientation_session.cc
+++ b/device/vr/orientation/orientation_session.cc
@@ -18,10 +18,12 @@
     : magic_window_receiver_(this, std::move(magic_window_receiver)),
       session_controller_receiver_(this, std::move(session_receiver)),
       device_(device) {
-  // Unretained is safe because the receiver will close when we are destroyed,
-  // so we won't receive any more callbacks after that.
-  session_controller_receiver_.set_disconnect_handler(base::BindOnce(
-      &VROrientationSession::OnMojoConnectionError, base::Unretained(this)));
+  magic_window_receiver_.set_disconnect_handler(
+      base::BindOnce(&VROrientationSession::OnMojoConnectionError,
+                     weak_ptr_factory_.GetWeakPtr()));
+  session_controller_receiver_.set_disconnect_handler(
+      base::BindOnce(&VROrientationSession::OnMojoConnectionError,
+                     weak_ptr_factory_.GetWeakPtr()));
 }
 
 VROrientationSession::~VROrientationSession() = default;
diff --git a/device/vr/orientation/orientation_session.h b/device/vr/orientation/orientation_session.h
index c5a822f..9e52db1 100644
--- a/device/vr/orientation/orientation_session.h
+++ b/device/vr/orientation/orientation_session.h
@@ -56,6 +56,9 @@
   mojo::Receiver<mojom::XRSessionController> session_controller_receiver_;
   device::VROrientationDevice* device_;
   bool restrict_frame_data_ = true;
+
+  // This must be the last member
+  base::WeakPtrFactory<VROrientationSession> weak_ptr_factory_{this};
 };
 
 }  // namespace device
diff --git a/docs/infra/cq_builders.md b/docs/infra/cq_builders.md
index 069eb7f2..579b5a5e 100644
--- a/docs/infra/cq_builders.md
+++ b/docs/infra/cq_builders.md
@@ -316,7 +316,7 @@
 
 * [android-marshmallow-arm64-coverage-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-marshmallow-arm64-coverage-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-marshmallow-arm64-coverage-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-marshmallow-arm64-coverage-rel))
 
-  * Experimental percentage: 3
+  * Experimental percentage: 20
 
 * [android-pie-arm64-rel](https://ci.chromium.org/p/chromium/builders/luci.chromium.try/android-pie-arm64-rel) ([`commit-queue.cfg` entry](https://cs.chromium.org/search/?q=package:%5Echromium$+file:commit-queue.cfg+chromium/try/android-pie-arm64-rel)) ([matching builders](https://cs.chromium.org/search/?q=file:trybots.py+android-pie-arm64-rel))
 
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
index 486fa90..160f862 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.cc
@@ -274,7 +274,8 @@
     const net::RedirectInfo& redirect_info,
     network::mojom::URLResponseHeadPtr head) {
   if (redirect_url_ != redirect_info.new_url &&
-      !IsRedirectSafe(request_.url, redirect_info.new_url)) {
+      !IsRedirectSafe(request_.url, redirect_info.new_url,
+                      info_->is_navigation_request)) {
     OnRequestError(
         network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT));
     return;
@@ -842,8 +843,11 @@
 // Determines whether it is safe to redirect from |from_url| to |to_url|.
 bool WebRequestProxyingURLLoaderFactory::InProgressRequest::IsRedirectSafe(
     const GURL& from_url,
-    const GURL& to_url) {
-  if (to_url.SchemeIs(extensions::kExtensionScheme)) {
+    const GURL& to_url,
+    bool is_navigation_request) {
+  // For navigations, non-web accessible resources will be blocked by
+  // ExtensionNavigationThrottle.
+  if (!is_navigation_request && to_url.SchemeIs(extensions::kExtensionScheme)) {
     const Extension* extension =
         ExtensionRegistry::Get(factory_->browser_context_)
             ->enabled_extensions()
diff --git a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
index a2614d79..0d62e501 100644
--- a/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
+++ b/extensions/browser/api/web_request/web_request_proxying_url_loader_factory.h
@@ -118,7 +118,9 @@
     void HandleResponseOrRedirectHeaders(
         net::CompletionOnceCallback continuation);
     void OnRequestError(const network::URLLoaderCompletionStatus& status);
-    bool IsRedirectSafe(const GURL& from_url, const GURL& to_url);
+    bool IsRedirectSafe(const GURL& from_url,
+                        const GURL& to_url,
+                        bool is_navigation_request);
     void HandleBeforeRequestRedirect();
 
     WebRequestProxyingURLLoaderFactory* const factory_;
diff --git a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
index ad6e32a..77bb4e9 100644
--- a/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
+++ b/extensions/renderer/api/automation/automation_internal_custom_bindings.cc
@@ -1847,7 +1847,7 @@
 }
 
 float AutomationInternalCustomBindings::GetDeviceScaleFactor() const {
-  return context()->GetRenderFrame()->GetRenderView()->GetDeviceScaleFactor();
+  return context()->GetRenderFrame()->GetDeviceScaleFactor();
 }
 
 void AutomationInternalCustomBindings::RouteTreeIDFunction(
diff --git a/infra/config/commit-queue.cfg b/infra/config/commit-queue.cfg
index a6babad..576d65f1 100644
--- a/infra/config/commit-queue.cfg
+++ b/infra/config/commit-queue.cfg
@@ -366,7 +366,7 @@
 
       builders {
         name: "chromium/try/android-marshmallow-arm64-coverage-rel"
-        experiment_percentage: 3
+        experiment_percentage: 20
       }
       builders {
         name: "chromium/try/android-pie-arm64-rel"
diff --git a/infra/config/generated/commit-queue.cfg b/infra/config/generated/commit-queue.cfg
index a6babad..576d65f1 100644
--- a/infra/config/generated/commit-queue.cfg
+++ b/infra/config/generated/commit-queue.cfg
@@ -366,7 +366,7 @@
 
       builders {
         name: "chromium/try/android-marshmallow-arm64-coverage-rel"
-        experiment_percentage: 3
+        experiment_percentage: 20
       }
       builders {
         name: "chromium/try/android-pie-arm64-rel"
diff --git a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
index 44acd764..cd00749 100644
--- a/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios13-beta-simulator.json
@@ -21,8 +21,8 @@
       "xcode parallelization": true,
       "include": "eg2_tests.json",
       "device type": "iPhone X",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "pool":"Chrome",
       "host os": "Mac-10.14.6"
     },
@@ -30,8 +30,8 @@
       "xcode parallelization": true,
       "include": "eg2_tests.json",
       "device type": "iPad (6th generation)",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "pool":"Chrome",
       "host os": "Mac-10.14.6"
     },
@@ -40,7 +40,7 @@
       "include": "eg2_tests.json",
       "device type": "iPhone X",
       "os": "12.2",
-      "xcode build version": "11a420a",
+      "xcode build version": "11a1027",
       "pool":"Chrome",
       "host os": "Mac-10.14.6"
     },
@@ -49,71 +49,71 @@
       "include": "eg2_tests.json",
       "device type": "iPad (6th generation)",
       "os": "12.2",
-      "xcode build version": "11a420a",
+      "xcode build version": "11a1027",
       "pool":"Chrome",
       "host os": "Mac-10.14.6"
     },
     {
       "include": "common_tests.json",
       "device type": "iPhone X",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     },
     {
       "include": "eg_cq_tests.json",
       "device type": "iPad Pro (12.9-inch)",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     },
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     },
     {
       "include": "eg_tests.json",
       "device type": "iPad Air (3rd generation)",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone X",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     },
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPad Air 2",
-      "os": "13.0",
-      "xcode build version": "11a420a",
+      "os": "13.1",
+      "xcode build version": "11a1027",
       "host os": "Mac-10.14.6",
       "pool":"Chrome"
     }
diff --git a/ios/build/bots/chromium.fyi/ios13-sdk-device.json b/ios/build/bots/chromium.fyi/ios13-sdk-device.json
index b588dbf2..602e908 100644
--- a/ios/build/bots/chromium.fyi/ios13-sdk-device.json
+++ b/ios/build/bots/chromium.fyi/ios13-sdk-device.json
@@ -3,7 +3,7 @@
     "Builder for Xcode 11 iOS 13 SDK",
     "Build is performed with gn+ninja."
   ],
-  "xcode build version": "11a420a",
+  "xcode build version": "11a1027",
   "gn_args": [
     "goma_dir=\"$(goma_dir)\"",
     "ios_enable_code_signing=false",
diff --git a/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json b/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
index 5bdafed9..c31e1c9 100644
--- a/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
+++ b/ios/build/bots/chromium.fyi/ios13-sdk-simulator.json
@@ -1,8 +1,8 @@
 {
   "comments": [
-    "Runs tests on 64-bit iOS 11.4 and 12.2 and 13.0 tests"
+    "Runs tests on 64-bit iOS 13.1 tests"
   ],
-  "xcode build version": "11a420a",
+  "xcode build version": "11a1027",
   "gn_args": [
     "goma_dir=\"$(goma_dir)\"",
     "is_component_build=false",
@@ -21,7 +21,8 @@
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s Plus",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
@@ -29,7 +30,8 @@
     {
       "include": "screen_size_dependent_tests.json",
       "device type": "iPhone 6s",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
@@ -37,7 +39,8 @@
     {
       "include": "common_tests.json",
       "device type": "iPhone 6s",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
@@ -45,7 +48,8 @@
     {
       "include": "eg_tests.json",
       "device type": "iPhone 7",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
@@ -53,7 +57,8 @@
     {
       "include": "eg_tests.json",
       "device type": "iPad Air 2",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
@@ -61,7 +66,8 @@
     {
       "include": "eg_tests.json",
       "device type": "iPhone X",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
@@ -69,7 +75,8 @@
     {
       "include": "eg_cq_tests.json",
       "device type": "iPhone X",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
@@ -77,7 +84,8 @@
     {
       "include": "eg_cq_tests.json",
       "device type": "iPad Air 2",
-      "os": "13.0",
+      "xcode build version": "11a1027",
+      "os": "13.1",
       "pool":"Chrome",
       "host os": "Mac-10.14.6",
       "priority": 30
diff --git a/ios/chrome/browser/ui/settings/password/BUILD.gn b/ios/chrome/browser/ui/settings/password/BUILD.gn
index ec2e9d4..243cc23 100644
--- a/ios/chrome/browser/ui/settings/password/BUILD.gn
+++ b/ios/chrome/browser/ui/settings/password/BUILD.gn
@@ -31,6 +31,7 @@
     "//ios/chrome/browser",
     "//ios/chrome/browser/browser_state",
     "//ios/chrome/browser/passwords",
+    "//ios/chrome/browser/signin",
     "//ios/chrome/browser/ui:feature_flags",
     "//ios/chrome/browser/ui/commands",
     "//ios/chrome/browser/ui/settings:settings_root",
diff --git a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
index febae5d..6a9ae1f 100644
--- a/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
+++ b/ios/chrome/browser/ui/settings/password/passwords_table_view_controller.mm
@@ -30,6 +30,8 @@
 #include "ios/chrome/browser/browser_state/chrome_browser_state.h"
 #include "ios/chrome/browser/passwords/ios_chrome_password_store_factory.h"
 #import "ios/chrome/browser/passwords/save_passwords_consumer.h"
+#import "ios/chrome/browser/signin/authentication_service.h"
+#include "ios/chrome/browser/signin/authentication_service_factory.h"
 #include "ios/chrome/browser/system_flags.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_cells_constants.h"
 #import "ios/chrome/browser/ui/settings/cells/settings_switch_cell.h"
@@ -464,6 +466,11 @@
   leakCheckItem.text = l10n_util::GetNSString(IDS_IOS_LEAK_CHECK_SWITCH);
   leakCheckItem.on = [passwordLeakCheckEnabled_ value];
   leakCheckItem.accessibilityIdentifier = @"leakCheckItem_switch";
+
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(browserState_);
+  leakCheckItem.enabled = authService->IsAuthenticated();
+
   return leakCheckItem;
 }
 
@@ -1243,11 +1250,14 @@
 }
 
 // Sets the leak check switch item's enabled status to |enabled| and
-// reconfigures the corresponding cell.
+// reconfigures the corresponding cell. If the user is not sign in, |enabled| is
+// overriden with |NO|.
 - (void)setLeakCheckSwitchItemEnabled:(BOOL)enabled {
   if (!base::FeatureList::IsEnabled(password_manager::features::kLeakDetection))
     return;
-  [leakCheckItem_ setEnabled:enabled];
+  AuthenticationService* authService =
+      AuthenticationServiceFactory::GetForBrowserState(browserState_);
+  [leakCheckItem_ setEnabled:enabled && authService->IsAuthenticated()];
   [self reconfigureCellsForItems:@[ leakCheckItem_ ]];
 }
 
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 50361c6..04b83c06 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -385,6 +385,16 @@
   ]
 }
 
+fuzzer_test("json_web_key_fuzzer") {
+  sources = [
+    "cdm/json_web_key_fuzzer.cc",
+  ]
+  deps = [
+    ":media",
+    "//base",
+  ]
+}
+
 if (proprietary_codecs) {
   fuzzer_test("media_mp4_avcc_parser_fuzzer") {
     sources = [
diff --git a/media/cdm/json_web_key_fuzzer.cc b/media/cdm/json_web_key_fuzzer.cc
new file mode 100644
index 0000000..e45c518
--- /dev/null
+++ b/media/cdm/json_web_key_fuzzer.cc
@@ -0,0 +1,36 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/cdm/json_web_key.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "base/logging.h"
+
+// For disabling noisy logging.
+struct Environment {
+  Environment() { logging::SetMinLogLevel(logging::LOG_FATAL); }
+};
+
+Environment* env = new Environment();
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+  std::vector<uint8_t> license(data, data + size);
+  std::vector<uint8_t> first_key;
+  media::ExtractFirstKeyIdFromLicenseRequest(license, &first_key);
+
+  std::string input(reinterpret_cast<const char*>(data), size);
+  media::KeyIdAndKeyPairs keys;
+  media::CdmSessionType session_type;
+  media::ExtractKeysFromJWKSet(input, &keys, &session_type);
+
+  media::KeyIdList key_ids;
+  std::string error_message;
+  media::ExtractKeyIdsFromKeyIdsInitData(input, &key_ids, &error_message);
+
+  return 0;
+}
diff --git a/media/learning/common/experiment_helper_unittest.cc b/media/learning/common/experiment_helper_unittest.cc
index 1864e3ea..16e98ae 100644
--- a/media/learning/common/experiment_helper_unittest.cc
+++ b/media/learning/common/experiment_helper_unittest.cc
@@ -20,8 +20,10 @@
   MockLearningTaskController(const LearningTask& task) : task_(task) {}
   ~MockLearningTaskController() override = default;
 
-  MOCK_METHOD2(BeginObservation,
-               void(base::UnguessableToken id, const FeatureVector& features));
+  MOCK_METHOD3(BeginObservation,
+               void(base::UnguessableToken id,
+                    const FeatureVector& features,
+                    const base::Optional<TargetValue>& default_value));
   MOCK_METHOD2(CompleteObservation,
                void(base::UnguessableToken id,
                     const ObservationCompletion& completion));
@@ -68,7 +70,7 @@
 };
 
 TEST_F(ExperimentHelperTest, BeginComplete) {
-  EXPECT_CALL(*controller_raw_, BeginObservation(_, _));
+  EXPECT_CALL(*controller_raw_, BeginObservation(_, _, _));
   helper_->BeginObservation(dict_);
   TargetValue target(123);
   EXPECT_CALL(*controller_raw_,
@@ -85,21 +87,21 @@
 }
 
 TEST_F(ExperimentHelperTest, BeginCancel) {
-  EXPECT_CALL(*controller_raw_, BeginObservation(_, _));
+  EXPECT_CALL(*controller_raw_, BeginObservation(_, _, _));
   helper_->BeginObservation(dict_);
   EXPECT_CALL(*controller_raw_, CancelObservation(_));
   helper_->CancelObservationIfNeeded();
 }
 
 TEST_F(ExperimentHelperTest, CompleteWithoutBeginDoesNothing) {
-  EXPECT_CALL(*controller_raw_, BeginObservation(_, _)).Times(0);
+  EXPECT_CALL(*controller_raw_, BeginObservation(_, _, _)).Times(0);
   EXPECT_CALL(*controller_raw_, CompleteObservation(_, _)).Times(0);
   EXPECT_CALL(*controller_raw_, CancelObservation(_)).Times(0);
   helper_->CompleteObservationIfNeeded(TargetValue(123));
 }
 
 TEST_F(ExperimentHelperTest, CancelWithoutBeginDoesNothing) {
-  EXPECT_CALL(*controller_raw_, BeginObservation(_, _)).Times(0);
+  EXPECT_CALL(*controller_raw_, BeginObservation(_, _, _)).Times(0);
   EXPECT_CALL(*controller_raw_, CompleteObservation(_, _)).Times(0);
   EXPECT_CALL(*controller_raw_, CancelObservation(_)).Times(0);
   helper_->CancelObservationIfNeeded();
diff --git a/media/learning/common/learning_task_controller.h b/media/learning/common/learning_task_controller.h
index f9cc2a7..195de4b 100644
--- a/media/learning/common/learning_task_controller.h
+++ b/media/learning/common/learning_task_controller.h
@@ -8,6 +8,7 @@
 #include "base/callback.h"
 #include "base/component_export.h"
 #include "base/macros.h"
+#include "base/optional.h"
 #include "base/unguessable_token.h"
 #include "media/learning/common/labelled_example.h"
 #include "media/learning/common/learning_task.h"
@@ -57,14 +58,19 @@
   // features into an example for training a model, then call
   // CompleteObservation with the same id and an ObservationCompletion.
   // Otherwise, call CancelObservation with |id|.  It's also okay to destroy the
-  // controller with outstanding observations; these will be cancelled.
+  // controller with outstanding observations; these will be cancelled if no
+  // |default_target| was specified, or completed with |default_target|.
+  //
   // TODO(liberato): This should optionally take a callback to receive a
   // prediction for the FeatureVector.
   // TODO(liberato): See if this ends up generating smaller code with pass-by-
   // value or with |FeatureVector&&|, once we have callers that can actually
   // benefit from it.
-  virtual void BeginObservation(base::UnguessableToken id,
-                                const FeatureVector& features) = 0;
+  virtual void BeginObservation(
+      base::UnguessableToken id,
+      const FeatureVector& features,
+      const base::Optional<TargetValue>& default_target =
+          base::Optional<TargetValue>()) = 0;
 
   // Complete an observation by sending a completion.
   virtual void CompleteObservation(base::UnguessableToken id,
diff --git a/media/learning/impl/learning_fuzzertest.cc b/media/learning/impl/learning_fuzzertest.cc
index 385cf874..fd55555 100644
--- a/media/learning/impl/learning_fuzzertest.cc
+++ b/media/learning/impl/learning_fuzzertest.cc
@@ -64,7 +64,11 @@
   // Build random examples.
   while (provider.remaining_bytes() > 0) {
     base::UnguessableToken id = base::UnguessableToken::Create();
-    controller.BeginObservation(id, ConsumeFeatureVector(&provider));
+    base::Optional<TargetValue> default_target;
+    if (provider.ConsumeBool())
+      default_target = TargetValue(ConsumeDouble(&provider));
+    controller.BeginObservation(id, ConsumeFeatureVector(&provider),
+                                default_target);
     controller.CompleteObservation(
         id, ObservationCompletion(TargetValue(ConsumeDouble(&provider)),
                                   ConsumeDouble(&provider)));
diff --git a/media/learning/impl/learning_session_impl.cc b/media/learning/impl/learning_session_impl.cc
index cbe0f5d..c87842eb 100644
--- a/media/learning/impl/learning_session_impl.cc
+++ b/media/learning/impl/learning_session_impl.cc
@@ -32,28 +32,41 @@
     if (!weak_session_)
       return;
 
-    // Cancel any outstanding observations.
-    for (auto& id : outstanding_ids_) {
-      controller_->Post(FROM_HERE, &LearningTaskController::CancelObservation,
-                        id);
+    // Cancel any outstanding observation, unless they have a default value.  In
+    // that case, complete them.
+    for (auto& id : outstanding_observations_) {
+      const base::Optional<TargetValue>& default_value = id.second;
+      if (default_value) {
+        controller_->Post(FROM_HERE,
+                          &LearningTaskController::CompleteObservation,
+                          id.first, *default_value);
+      } else {
+        controller_->Post(FROM_HERE, &LearningTaskController::CancelObservation,
+                          id.first);
+      }
     }
   }
 
-  void BeginObservation(base::UnguessableToken id,
-                        const FeatureVector& features) override {
+  void BeginObservation(
+      base::UnguessableToken id,
+      const FeatureVector& features,
+      const base::Optional<TargetValue>& default_target) override {
     if (!weak_session_)
       return;
 
-    outstanding_ids_.insert(id);
+    outstanding_observations_[id] = default_target;
+    // We don't send along the default value because LearningTaskControllerImpl
+    // doesn't support it.  Since all client calls eventually come through us
+    // anyway, it seems okay to handle it here.
     controller_->Post(FROM_HERE, &LearningTaskController::BeginObservation, id,
-                      features);
+                      features, base::nullopt);
   }
 
   void CompleteObservation(base::UnguessableToken id,
                            const ObservationCompletion& completion) override {
     if (!weak_session_)
       return;
-    outstanding_ids_.erase(id);
+    outstanding_observations_.erase(id);
     controller_->Post(FROM_HERE, &LearningTaskController::CompleteObservation,
                       id, completion);
   }
@@ -61,7 +74,7 @@
   void CancelObservation(base::UnguessableToken id) override {
     if (!weak_session_)
       return;
-    outstanding_ids_.erase(id);
+    outstanding_observations_.erase(id);
     controller_->Post(FROM_HERE, &LearningTaskController::CancelObservation,
                       id);
   }
@@ -72,8 +85,10 @@
   base::SequenceBound<LearningTaskController>* controller_;
   LearningTask task_;
 
-  // Set of ids that have been started but not completed / cancelled yet.
-  std::set<base::UnguessableToken> outstanding_ids_;
+  // Set of ids that have been started but not completed / cancelled yet, and
+  // any default target value.
+  std::map<base::UnguessableToken, base::Optional<TargetValue>>
+      outstanding_observations_;
 };
 
 LearningSessionImpl::LearningSessionImpl(
diff --git a/media/learning/impl/learning_session_impl_unittest.cc b/media/learning/impl/learning_session_impl_unittest.cc
index f8f2301..5c82e9a 100644
--- a/media/learning/impl/learning_session_impl_unittest.cc
+++ b/media/learning/impl/learning_session_impl_unittest.cc
@@ -40,10 +40,13 @@
       }
     }
 
-    void BeginObservation(base::UnguessableToken id,
-                          const FeatureVector& features) override {
+    void BeginObservation(
+        base::UnguessableToken id,
+        const FeatureVector& features,
+        const base::Optional<TargetValue>& default_target) override {
       id_ = id;
       features_ = features;
+      default_target_ = default_target;
     }
 
     void CompleteObservation(base::UnguessableToken id,
@@ -66,6 +69,7 @@
     SequenceBoundFeatureProvider feature_provider_;
     base::UnguessableToken id_;
     FeatureVector features_;
+    base::Optional<TargetValue> default_target_;
     LabelledExample example_;
 
     // Most recently cancelled id.
@@ -230,5 +234,29 @@
   EXPECT_EQ(task_controllers_[0]->cancelled_id_, id);
 }
 
+TEST_F(LearningSessionImplTest,
+       DestroyingControllerCompletesObservationsWithDefaultValues) {
+  // Also verifies that we don't send the default to the underlying controller,
+  // because LearningTaskControllerImpl doesn't support it.
+  session_->RegisterTask(task_0_);
+
+  std::unique_ptr<LearningTaskController> controller =
+      session_->GetController(task_0_.name);
+  task_environment_.RunUntilIdle();
+
+  // Start an observation and verify that it doesn't forward the default target.
+  base::UnguessableToken id = base::UnguessableToken::Create();
+  TargetValue default_target(123);
+  controller->BeginObservation(id, FeatureVector(), default_target);
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(task_controllers_[0]->id_, id);
+  EXPECT_FALSE(task_controllers_[0]->default_target_);
+
+  // Should result in completes the observation.
+  controller.reset();
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(task_controllers_[0]->example_.target_value, default_target);
+}
+
 }  // namespace learning
 }  // namespace media
diff --git a/media/learning/impl/learning_task_controller_impl.cc b/media/learning/impl/learning_task_controller_impl.cc
index c9bb0f3..9d71bb8 100644
--- a/media/learning/impl/learning_task_controller_impl.cc
+++ b/media/learning/impl/learning_task_controller_impl.cc
@@ -49,7 +49,8 @@
 
 void LearningTaskControllerImpl::BeginObservation(
     base::UnguessableToken id,
-    const FeatureVector& features) {
+    const FeatureVector& features,
+    const base::Optional<TargetValue>& default_target) {
   // TODO(liberato): Should we enforce that the right number of features are
   // present here?  Right now, we allow it to be shorter, so that features from
   // a FeatureProvider may be omitted.  Of course, they have to be at the end in
@@ -58,6 +59,12 @@
   if (!trainer_)
     return;
 
+  // We don't support default targets, since we're the base learner and can't
+  // easily do that.  However, defaults are handled by (weak) controllers
+  // handed out by LearningSessionImpl.  So, we don't bother since they never
+  // get here anyway.
+  DCHECK(!default_target);
+
   helper_->BeginObservation(id, features);
 }
 
diff --git a/media/learning/impl/learning_task_controller_impl.h b/media/learning/impl/learning_task_controller_impl.h
index ae011fd..a3e69680 100644
--- a/media/learning/impl/learning_task_controller_impl.h
+++ b/media/learning/impl/learning_task_controller_impl.h
@@ -46,8 +46,15 @@
   ~LearningTaskControllerImpl() override;
 
   // LearningTaskController
-  void BeginObservation(base::UnguessableToken id,
-                        const FeatureVector& features) override;
+  // Note that we don't support |default_target|, since destroying us destroys
+  // everything.  One might make the argument that only the mojo client /
+  // service should support default values, but it's much more convenient if
+  // they're part of the base api.  So, since clients shouldn't be dealing with
+  // us directly (see LearningSessionImpl), it's okay.
+  void BeginObservation(
+      base::UnguessableToken id,
+      const FeatureVector& features,
+      const base::Optional<TargetValue>& default_target) override;
   void CompleteObservation(base::UnguessableToken id,
                            const ObservationCompletion& completion) override;
   void CancelObservation(base::UnguessableToken id) override;
diff --git a/media/learning/impl/learning_task_controller_impl_unittest.cc b/media/learning/impl/learning_task_controller_impl_unittest.cc
index 0faa166b..30a6558 100644
--- a/media/learning/impl/learning_task_controller_impl_unittest.cc
+++ b/media/learning/impl/learning_task_controller_impl_unittest.cc
@@ -132,7 +132,7 @@
 
   void AddExample(const LabelledExample& example) {
     base::UnguessableToken id = base::UnguessableToken::Create();
-    controller_->BeginObservation(id, example.features);
+    controller_->BeginObservation(id, example.features, base::nullopt);
     controller_->CompleteObservation(
         id, ObservationCompletion(example.target_value, example.weight));
   }
diff --git a/media/learning/mojo/mojo_learning_task_controller_service.cc b/media/learning/mojo/mojo_learning_task_controller_service.cc
index acd3f31..93d1edcc 100644
--- a/media/learning/mojo/mojo_learning_task_controller_service.cc
+++ b/media/learning/mojo/mojo_learning_task_controller_service.cc
@@ -25,7 +25,8 @@
 
 void MojoLearningTaskControllerService::BeginObservation(
     const base::UnguessableToken& id,
-    const FeatureVector& features) {
+    const FeatureVector& features,
+    const base::Optional<TargetValue>& default_target) {
   // Drop the observation if it doesn't match the feature description size.
   if (features.size() != task_.feature_descriptions.size())
     return;
@@ -37,7 +38,7 @@
 
   // Since we own |impl_|, we don't need to keep track of in-flight
   // observations.  We'll release |impl_| on destruction, which cancels them.
-  impl_->BeginObservation(id, features);
+  impl_->BeginObservation(id, features, default_target);
 }
 
 void MojoLearningTaskControllerService::CompleteObservation(
diff --git a/media/learning/mojo/mojo_learning_task_controller_service.h b/media/learning/mojo/mojo_learning_task_controller_service.h
index cdf8721..7a41e2f 100644
--- a/media/learning/mojo/mojo_learning_task_controller_service.h
+++ b/media/learning/mojo/mojo_learning_task_controller_service.h
@@ -28,8 +28,10 @@
   ~MojoLearningTaskControllerService() override;
 
   // mojom::LearningTaskController
-  void BeginObservation(const base::UnguessableToken& id,
-                        const FeatureVector& features) override;
+  void BeginObservation(
+      const base::UnguessableToken& id,
+      const FeatureVector& features,
+      const base::Optional<TargetValue>& default_target) override;
   void CompleteObservation(const base::UnguessableToken& id,
                            const ObservationCompletion& completion) override;
   void CancelObservation(const base::UnguessableToken& id) override;
diff --git a/media/learning/mojo/mojo_learning_task_controller_service_unittest.cc b/media/learning/mojo/mojo_learning_task_controller_service_unittest.cc
index b2a38ad..eecf3a5d 100644
--- a/media/learning/mojo/mojo_learning_task_controller_service_unittest.cc
+++ b/media/learning/mojo/mojo_learning_task_controller_service_unittest.cc
@@ -20,10 +20,13 @@
  public:
   class FakeLearningTaskController : public LearningTaskController {
    public:
-    void BeginObservation(base::UnguessableToken id,
-                          const FeatureVector& features) override {
+    void BeginObservation(
+        base::UnguessableToken id,
+        const FeatureVector& features,
+        const base::Optional<TargetValue>& default_target) override {
       begin_args_.id_ = id;
       begin_args_.features_ = features;
+      begin_args_.default_target_ = std::move(default_target);
     }
 
     void CompleteObservation(base::UnguessableToken id,
@@ -43,6 +46,7 @@
     struct {
       base::UnguessableToken id_;
       FeatureVector features_;
+      base::Optional<TargetValue> default_target_;
     } begin_args_;
 
     struct {
@@ -87,9 +91,10 @@
 TEST_F(MojoLearningTaskControllerServiceTest, BeginComplete) {
   base::UnguessableToken id = base::UnguessableToken::Create();
   FeatureVector features = {FeatureValue(123), FeatureValue(456)};
-  service_->BeginObservation(id, features);
+  service_->BeginObservation(id, features, base::nullopt);
   EXPECT_EQ(id, controller_raw_->begin_args_.id_);
   EXPECT_EQ(features, controller_raw_->begin_args_.features_);
+  EXPECT_FALSE(controller_raw_->begin_args_.default_target_);
 
   ObservationCompletion completion(TargetValue(1234));
   service_->CompleteObservation(id, completion);
@@ -102,20 +107,31 @@
 TEST_F(MojoLearningTaskControllerServiceTest, BeginCancel) {
   base::UnguessableToken id = base::UnguessableToken::Create();
   FeatureVector features = {FeatureValue(123), FeatureValue(456)};
-  service_->BeginObservation(id, features);
+  service_->BeginObservation(id, features, base::nullopt);
   EXPECT_EQ(id, controller_raw_->begin_args_.id_);
   EXPECT_EQ(features, controller_raw_->begin_args_.features_);
+  EXPECT_FALSE(controller_raw_->begin_args_.default_target_);
 
   service_->CancelObservation(id);
 
   EXPECT_EQ(id, controller_raw_->cancel_args_.id_);
 }
 
+TEST_F(MojoLearningTaskControllerServiceTest, BeginWithDefaultTarget) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+  FeatureVector features = {FeatureValue(123), FeatureValue(456)};
+  TargetValue default_target(987);
+  service_->BeginObservation(id, features, default_target);
+  EXPECT_EQ(id, controller_raw_->begin_args_.id_);
+  EXPECT_EQ(features, controller_raw_->begin_args_.features_);
+  EXPECT_EQ(default_target, controller_raw_->begin_args_.default_target_);
+}
+
 TEST_F(MojoLearningTaskControllerServiceTest, TooFewFeaturesIsIgnored) {
   // A FeatureVector with too few elements should be ignored.
   base::UnguessableToken id = base::UnguessableToken::Create();
   FeatureVector short_features = {FeatureValue(123)};
-  service_->BeginObservation(id, short_features);
+  service_->BeginObservation(id, short_features, base::nullopt);
   EXPECT_NE(id, controller_raw_->begin_args_.id_);
   EXPECT_EQ(controller_raw_->begin_args_.features_.size(), 0u);
 }
@@ -125,7 +141,7 @@
   base::UnguessableToken id = base::UnguessableToken::Create();
   FeatureVector long_features = {FeatureValue(123), FeatureValue(456),
                                  FeatureValue(789)};
-  service_->BeginObservation(id, long_features);
+  service_->BeginObservation(id, long_features, base::nullopt);
   EXPECT_NE(id, controller_raw_->begin_args_.id_);
   EXPECT_EQ(controller_raw_->begin_args_.features_.size(), 0u);
 }
diff --git a/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc b/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc
index a54e5d6..46e9a16 100644
--- a/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc
+++ b/media/learning/mojo/public/cpp/mojo_learning_task_controller.cc
@@ -20,10 +20,11 @@
 
 void MojoLearningTaskController::BeginObservation(
     base::UnguessableToken id,
-    const FeatureVector& features) {
+    const FeatureVector& features,
+    const base::Optional<TargetValue>& default_target) {
   // We don't need to keep track of in-flight observations, since the service
   // side handles it for us.
-  controller_ptr_->BeginObservation(id, features);
+  controller_ptr_->BeginObservation(id, features, default_target);
 }
 
 void MojoLearningTaskController::CompleteObservation(
diff --git a/media/learning/mojo/public/cpp/mojo_learning_task_controller.h b/media/learning/mojo/public/cpp/mojo_learning_task_controller.h
index d76ede6..81d432e 100644
--- a/media/learning/mojo/public/cpp/mojo_learning_task_controller.h
+++ b/media/learning/mojo/public/cpp/mojo_learning_task_controller.h
@@ -26,8 +26,10 @@
   ~MojoLearningTaskController() override;
 
   // LearningTaskController
-  void BeginObservation(base::UnguessableToken id,
-                        const FeatureVector& features) override;
+  void BeginObservation(
+      base::UnguessableToken id,
+      const FeatureVector& features,
+      const base::Optional<TargetValue>& default_target) override;
   void CompleteObservation(base::UnguessableToken id,
                            const ObservationCompletion& completion) override;
   void CancelObservation(base::UnguessableToken id) override;
diff --git a/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc b/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc
index 716e826..93a16f1 100644
--- a/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc
+++ b/media/learning/mojo/public/cpp/mojo_learning_task_controller_unittest.cc
@@ -22,10 +22,13 @@
   // Impl of a mojom::LearningTaskController that remembers call arguments.
   class FakeMojoLearningTaskController : public mojom::LearningTaskController {
    public:
-    void BeginObservation(const base::UnguessableToken& id,
-                          const FeatureVector& features) override {
+    void BeginObservation(
+        const base::UnguessableToken& id,
+        const FeatureVector& features,
+        const base::Optional<TargetValue>& default_target) override {
       begin_args_.id_ = id;
       begin_args_.features_ = features;
+      begin_args_.default_target_ = default_target;
     }
 
     void CompleteObservation(const base::UnguessableToken& id,
@@ -41,6 +44,7 @@
     struct {
       base::UnguessableToken id_;
       FeatureVector features_;
+      base::Optional<TargetValue> default_target_;
     } begin_args_;
 
     struct {
@@ -87,13 +91,26 @@
   EXPECT_EQ(learning_controller_->GetLearningTask().name, task_.name);
 }
 
-TEST_F(MojoLearningTaskControllerTest, Begin) {
+TEST_F(MojoLearningTaskControllerTest, BeginWithoutDefaultTarget) {
   base::UnguessableToken id = base::UnguessableToken::Create();
   FeatureVector features = {FeatureValue(123), FeatureValue(456)};
-  learning_controller_->BeginObservation(id, features);
+  learning_controller_->BeginObservation(id, features, base::nullopt);
   task_environment_.RunUntilIdle();
   EXPECT_EQ(id, fake_learning_controller_.begin_args_.id_);
   EXPECT_EQ(features, fake_learning_controller_.begin_args_.features_);
+  EXPECT_FALSE(fake_learning_controller_.begin_args_.default_target_);
+}
+
+TEST_F(MojoLearningTaskControllerTest, BeginWithDefaultTarget) {
+  base::UnguessableToken id = base::UnguessableToken::Create();
+  TargetValue default_target(987);
+  FeatureVector features = {FeatureValue(123), FeatureValue(456)};
+  learning_controller_->BeginObservation(id, features, default_target);
+  task_environment_.RunUntilIdle();
+  EXPECT_EQ(id, fake_learning_controller_.begin_args_.id_);
+  EXPECT_EQ(features, fake_learning_controller_.begin_args_.features_);
+  EXPECT_EQ(default_target,
+            fake_learning_controller_.begin_args_.default_target_);
 }
 
 TEST_F(MojoLearningTaskControllerTest, Complete) {
diff --git a/media/learning/mojo/public/mojom/learning_task_controller.mojom b/media/learning/mojo/public/mojom/learning_task_controller.mojom
index 8fb3135..23123f5 100644
--- a/media/learning/mojo/public/mojom/learning_task_controller.mojom
+++ b/media/learning/mojo/public/mojom/learning_task_controller.mojom
@@ -23,9 +23,12 @@
   // features into an example for training a model, then call
   // CompleteObservation with the same id and an ObservationCompletion.
   // Otherwise, call CancelObservation with |id|.  It's also okay to destroy the
-  // controller with outstanding observations; these will be cancelled.
+  // controller with outstanding observations; these will be cancelled if no
+  // |default_target| was specified, else they will be completed using that as
+  // the target value.
   BeginObservation(mojo_base.mojom.UnguessableToken id,
-          array<FeatureValue> features);
+          array<FeatureValue> features,
+          TargetValue? default_target);
 
   // Complete observation |id| by providing |completion|.
   CompleteObservation(mojo_base.mojom.UnguessableToken id,
diff --git a/media/mojo/README.md b/media/mojo/README.md
index 963a30d7..d5097dc 100644
--- a/media/mojo/README.md
+++ b/media/mojo/README.md
@@ -327,6 +327,26 @@
 * `ProvisionFetcher`: for Android MediaDrm device provisioning
 * `CdmProxy`: (in progress)
 
+### Security
+
+In most cases, the client side runs in the renderer process which is the least
+trusted. Also always assume the client side code may be compromised, e.g. making
+calls in random order or passing in garbage parameters.
+
+Due to the [Flexible Process Model](#Flexible-Process-Model), it's sometimes
+hard to know in which process the service side runs. As a rule of thumb, assume
+all service side code may run in a privileged process (e.g. browser process),
+including the common supporting code like `MojoVideoDecoderService`, as well as
+the concrete [Media Component](#Media-Components), e.g. MediaCodecVideoDecoder
+on Android.  To know exactly which [Media Component](#Media-Components) runs in
+which process in production, see [Adoption](#Adoption) below.
+
+Also note that all the [Secure Auxiliary Services](#Secure-Auxiliary-Services)
+are running in a more privileged process than the process where the media
+components that use them run in. For example, all of the existing services run
+in the browser process except for the `CdmProxy`, which runs in the GPU process.
+They must defend against compromised media components.
+
 ### Adoption
 
 #### Android
diff --git a/net/base/directory_listing.cc b/net/base/directory_listing.cc
index da9061a..a0e66ef 100644
--- a/net/base/directory_listing.cc
+++ b/net/base/directory_listing.cc
@@ -7,7 +7,7 @@
 #include "base/i18n/time_formatting.h"
 #include "base/json/string_escape.h"
 #include "base/logging.h"
-#include "base/strings/string_piece.h"
+#include "base/memory/ref_counted_memory.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
@@ -18,15 +18,14 @@
 namespace net {
 
 std::string GetDirectoryListingHeader(const base::string16& title) {
-  static const base::StringPiece header(
+  scoped_refptr<base::RefCountedMemory> header(
       NetModule::GetResource(IDR_DIR_HEADER_HTML));
   // This can be null in unit tests.
-  DLOG_IF(WARNING, header.empty())
-      << "Missing resource: directory listing header";
+  DLOG_IF(WARNING, !header) << "Missing resource: directory listing header";
 
   std::string result;
-  if (!header.empty())
-    result.assign(header.data(), header.size());
+  if (header)
+    result.assign(header->front_as<char>(), header->size());
 
   result.append("<script>start(");
   base::EscapeJSONString(title, true, &result);
diff --git a/net/base/net_module.cc b/net/base/net_module.cc
index d7f1918..0425174 100644
--- a/net/base/net_module.cc
+++ b/net/base/net_module.cc
@@ -14,8 +14,8 @@
 }
 
 // static
-base::StringPiece NetModule::GetResource(int key) {
-  return resource_provider ? resource_provider(key) : base::StringPiece();
+scoped_refptr<base::RefCountedMemory> NetModule::GetResource(int key) {
+  return resource_provider ? resource_provider(key) : nullptr;
 }
 
 }  // namespace net
diff --git a/net/base/net_module.h b/net/base/net_module.h
index 40359b25..c747ece 100644
--- a/net/base/net_module.h
+++ b/net/base/net_module.h
@@ -6,7 +6,7 @@
 #define NET_BASE_NET_MODULE_H__
 
 #include "base/macros.h"
-#include "base/strings/string_piece.h"
+#include "base/memory/ref_counted_memory.h"
 #include "net/base/net_export.h"
 
 namespace net {
@@ -20,15 +20,15 @@
 //
 class NET_EXPORT NetModule {
  public:
-  typedef base::StringPiece (*ResourceProvider)(int key);
+  typedef scoped_refptr<base::RefCountedMemory> (*ResourceProvider)(int key);
 
   // Set the function to call when the net module needs resources
   static void SetResourceProvider(ResourceProvider func);
 
   // Call the resource provider (if one exists) to get the specified resource.
-  // Returns an empty string if the resource does not exist or if there is no
-  // resource provider.
-  static base::StringPiece GetResource(int key);
+  // Returns nullptr if the resource does not exist or if there is no resource
+  // provider.
+  static scoped_refptr<base::RefCountedMemory> GetResource(int key);
 
  private:
   DISALLOW_IMPLICIT_CONSTRUCTORS(NetModule);
diff --git a/net/base/net_resources.grd b/net/base/net_resources.grd
index 2937014..45d1926 100644
--- a/net/base/net_resources.grd
+++ b/net/base/net_resources.grd
@@ -9,7 +9,7 @@
   <translations />
   <release seq="1">
     <includes>
-      <include name="IDR_DIR_HEADER_HTML" file="dir_header.html" type="BINDATA" />
+      <include name="IDR_DIR_HEADER_HTML" file="dir_header.html" type="BINDATA" compress="gzip" />
     </includes>
   </release>
 </grit>
diff --git a/net/cert/nss_cert_database.cc b/net/cert/nss_cert_database.cc
index d940688..34ad46d 100644
--- a/net/cert/nss_cert_database.cc
+++ b/net/cert/nss_cert_database.cc
@@ -82,10 +82,6 @@
 
 NSSCertDatabase::~NSSCertDatabase() = default;
 
-ScopedCERTCertificateList NSSCertDatabase::ListCertsSync() {
-  return ListCertsImpl(crypto::ScopedPK11Slot());
-}
-
 void NSSCertDatabase::ListCerts(ListCertsCallback callback) {
   base::PostTaskAndReplyWithResult(
       FROM_HERE,
diff --git a/net/cert/nss_cert_database.h b/net/cert/nss_cert_database.h
index 97be8c4..d8a0914 100644
--- a/net/cert/nss_cert_database.h
+++ b/net/cert/nss_cert_database.h
@@ -106,11 +106,6 @@
                   crypto::ScopedPK11Slot private_slot);
   virtual ~NSSCertDatabase();
 
-  // Get a list of unique certificates in the certificate database (one
-  // instance of all certificates).
-  // DEPRECATED by |ListCerts|. See http://crbug.com/340460.
-  virtual ScopedCERTCertificateList ListCertsSync();
-
   // Asynchronously get a list of unique certificates in the certificate
   // database (one instance of all certificates). Note that the callback may be
   // run even after the database is deleted.
@@ -240,10 +235,9 @@
   bool IsHardwareBacked(const CERTCertificate* cert) const;
 
  protected:
-  // Certificate listing implementation used by |ListCerts*| and
-  // |ListCertsSync|. Static so it may safely be used on the worker thread.
-  // If |slot| is NULL, obtains the certs of all slots, otherwise only of
-  // |slot|.
+  // Certificate listing implementation used by |ListCerts*|. Static so it may
+  // safely be used on the worker thread. If |slot| is nullptr, obtains the
+  // certs of all slots, otherwise only of |slot|.
   static ScopedCERTCertificateList ListCertsImpl(crypto::ScopedPK11Slot slot);
 
   // Broadcasts notifications to all registered observers.
diff --git a/net/cert/nss_cert_database_chromeos.cc b/net/cert/nss_cert_database_chromeos.cc
index d32c5a1..94bcdaf 100644
--- a/net/cert/nss_cert_database_chromeos.cc
+++ b/net/cert/nss_cert_database_chromeos.cc
@@ -39,10 +39,6 @@
   profile_filter_.Init(GetPublicSlot(), GetPrivateSlot(), GetSystemSlot());
 }
 
-ScopedCERTCertificateList NSSCertDatabaseChromeOS::ListCertsSync() {
-  return ListCertsImpl(profile_filter_);
-}
-
 void NSSCertDatabaseChromeOS::ListCerts(
     NSSCertDatabase::ListCertsCallback callback) {
   base::PostTaskAndReplyWithResult(
diff --git a/net/cert/nss_cert_database_chromeos.h b/net/cert/nss_cert_database_chromeos.h
index 7ffb85209..f4cfd2d 100644
--- a/net/cert/nss_cert_database_chromeos.h
+++ b/net/cert/nss_cert_database_chromeos.h
@@ -26,7 +26,6 @@
   void SetSystemSlot(crypto::ScopedPK11Slot system_slot);
 
   // NSSCertDatabase implementation.
-  ScopedCERTCertificateList ListCertsSync() override;
   void ListCerts(NSSCertDatabase::ListCertsCallback callback) override;
   void ListModules(std::vector<crypto::ScopedPK11Slot>* modules,
                    bool need_rw) const override;
@@ -37,7 +36,7 @@
   // TODO(mattm): handle trust setting correctly for certs in read-only slots.
 
  private:
-  // Certificate listing implementation used by |ListCerts| and |ListCertsSync|.
+  // Certificate listing implementation used by |ListCerts|.
   // The certificate list normally returned by NSSCertDatabase::ListCertsImpl
   // is additionally filtered by |profile_filter|.
   // Static so it may safely be used on the worker thread.
diff --git a/net/cert/nss_cert_database_chromeos_unittest.cc b/net/cert/nss_cert_database_chromeos_unittest.cc
index 1e4dcca..bcec29f 100644
--- a/net/cert/nss_cert_database_chromeos_unittest.cc
+++ b/net/cert/nss_cert_database_chromeos_unittest.cc
@@ -8,6 +8,7 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/run_loop.h"
 #include "crypto/nss_util_internal.h"
 #include "crypto/scoped_test_nss_chromeos_user.h"
 #include "crypto/scoped_test_nss_db.h"
@@ -159,39 +160,24 @@
   EXPECT_EQ(0U, failed.size());
 
   // Get cert list for each user.
-  ScopedCERTCertificateList user_1_certlist = db_1_->ListCertsSync();
-  ScopedCERTCertificateList user_2_certlist = db_2_->ListCertsSync();
+  ScopedCERTCertificateList user_1_certlist;
+  ScopedCERTCertificateList user_2_certlist;
+  db_1_->ListCerts(
+      base::BindOnce(&SwapCertLists, base::Unretained(&user_1_certlist)));
+  db_2_->ListCerts(
+      base::BindOnce(&SwapCertLists, base::Unretained(&user_2_certlist)));
 
-  // Check that the imported certs only shows up in the list for the user that
-  // imported them.
+  // Run the message loop so the observer notifications get processed and
+  // lookups are completed.
+  RunUntilIdle();
+  // Should have gotten two OnCertDBChanged notifications.
+  ASSERT_EQ(2, db_changed_count_);
+
   EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist));
   EXPECT_FALSE(IsCertInCertificateList(certs_1[0].get(), user_2_certlist));
 
   EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist));
   EXPECT_FALSE(IsCertInCertificateList(certs_2[0].get(), user_1_certlist));
-
-  // Run the message loop so the observer notifications get processed.
-  RunUntilIdle();
-  // Should have gotten two OnCertDBChanged notifications.
-  ASSERT_EQ(2, db_changed_count_);
-
-  // Tests that the new certs are loaded by async ListCerts method.
-  ScopedCERTCertificateList user_1_certlist_async;
-  ScopedCERTCertificateList user_2_certlist_async;
-  db_1_->ListCerts(
-      base::BindOnce(&SwapCertLists, base::Unretained(&user_1_certlist_async)));
-  db_2_->ListCerts(
-      base::BindOnce(&SwapCertLists, base::Unretained(&user_2_certlist_async)));
-
-  RunUntilIdle();
-
-  EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist_async));
-  EXPECT_FALSE(
-      IsCertInCertificateList(certs_1[0].get(), user_2_certlist_async));
-
-  EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist_async));
-  EXPECT_FALSE(
-      IsCertInCertificateList(certs_2[0].get(), user_1_certlist_async));
 }
 
 // Test that ImportServerCerts imports the cert to the correct slot, and that
@@ -219,40 +205,25 @@
   EXPECT_EQ(0U, failed.size());
 
   // Get cert list for each user.
-  ScopedCERTCertificateList user_1_certlist = db_1_->ListCertsSync();
-  ScopedCERTCertificateList user_2_certlist = db_2_->ListCertsSync();
+  ScopedCERTCertificateList user_1_certlist;
+  ScopedCERTCertificateList user_2_certlist;
+  db_1_->ListCerts(
+      base::BindOnce(&SwapCertLists, base::Unretained(&user_1_certlist)));
+  db_2_->ListCerts(
+      base::BindOnce(&SwapCertLists, base::Unretained(&user_2_certlist)));
 
-  // Check that the imported certs only shows up in the list for the user that
-  // imported them.
-  EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist));
-  EXPECT_FALSE(IsCertInCertificateList(certs_1[0].get(), user_2_certlist));
-
-  EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist));
-  EXPECT_FALSE(IsCertInCertificateList(certs_2[0].get(), user_1_certlist));
-
-  // Run the message loop so the observer notifications get processed.
+  // Run the message loop so the observer notifications get processed and
+  // lookups are completed.
   RunUntilIdle();
   // TODO(mattm): ImportServerCert doesn't actually cause any observers to
   // fire. Is that correct?
   EXPECT_EQ(0, db_changed_count_);
 
-  // Tests that the new certs are loaded by async ListCerts method.
-  ScopedCERTCertificateList user_1_certlist_async;
-  ScopedCERTCertificateList user_2_certlist_async;
-  db_1_->ListCerts(
-      base::BindOnce(&SwapCertLists, base::Unretained(&user_1_certlist_async)));
-  db_2_->ListCerts(
-      base::BindOnce(&SwapCertLists, base::Unretained(&user_2_certlist_async)));
+  EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist));
+  EXPECT_FALSE(IsCertInCertificateList(certs_1[0].get(), user_2_certlist));
 
-  RunUntilIdle();
-
-  EXPECT_TRUE(IsCertInCertificateList(certs_1[0].get(), user_1_certlist_async));
-  EXPECT_FALSE(
-      IsCertInCertificateList(certs_1[0].get(), user_2_certlist_async));
-
-  EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist_async));
-  EXPECT_FALSE(
-      IsCertInCertificateList(certs_2[0].get(), user_1_certlist_async));
+  EXPECT_TRUE(IsCertInCertificateList(certs_2[0].get(), user_2_certlist));
+  EXPECT_FALSE(IsCertInCertificateList(certs_2[0].get(), user_1_certlist));
 }
 
 // Tests that There is no crash if the database is deleted while ListCerts
@@ -281,7 +252,10 @@
                                      "client_2.pem",
                                      "client_2.pk8",
                                      db_1_->GetSystemSlot().get()));
-  ScopedCERTCertificateList certs = db_1_->ListCertsSync();
+
+  ScopedCERTCertificateList certs;
+  db_1_->ListCerts(base::BindOnce(&SwapCertLists, base::Unretained(&certs)));
+  RunUntilIdle();
   EXPECT_TRUE(IsCertInCertificateList(cert_1.get(), certs));
   EXPECT_TRUE(IsCertInCertificateList(cert_2.get(), certs));
 }
@@ -298,7 +272,9 @@
                                      "client_2.pem",
                                      "client_2.pk8",
                                      system_db_.slot()));
-  ScopedCERTCertificateList certs = db_2_->ListCertsSync();
+  ScopedCERTCertificateList certs;
+  db_2_->ListCerts(base::BindOnce(&SwapCertLists, base::Unretained(&certs)));
+  RunUntilIdle();
   EXPECT_TRUE(IsCertInCertificateList(cert_1.get(), certs));
   EXPECT_FALSE(IsCertInCertificateList(cert_2.get(), certs));
 }
diff --git a/net/cert/nss_cert_database_unittest.cc b/net/cert/nss_cert_database_unittest.cc
index 56e952a..56b5cf7 100644
--- a/net/cert/nss_cert_database_unittest.cc
+++ b/net/cert/nss_cert_database_unittest.cc
@@ -134,15 +134,6 @@
   scoped_refptr<CRLSet> crl_set_;
 };
 
-TEST_F(CertDatabaseNSSTest, ListCertsSync) {
-  // This test isn't terribly useful, though it might help with memory
-  // leak tests.
-  ScopedCERTCertificateList certs = cert_db_->ListCertsSync();
-  // The test DB is empty, but let's assume there will always be something in
-  // the other slots.
-  EXPECT_LT(0U, certs.size());
-}
-
 TEST_F(CertDatabaseNSSTest, ListCerts) {
   // This test isn't terribly useful, though it might help with memory
   // leak tests.
diff --git a/net/dns/context_host_resolver.cc b/net/dns/context_host_resolver.cc
index 6f307a3..444141a 100644
--- a/net/dns/context_host_resolver.cc
+++ b/net/dns/context_host_resolver.cc
@@ -184,6 +184,9 @@
   for (auto* active_request : handed_out_requests_)
     active_request->OnShutdown();
 
+  DCHECK(context_);
+  manager_->CancelProbesForContext(context_);
+
   context_ = nullptr;
   shutting_down_ = true;
 
diff --git a/net/dns/context_host_resolver_unittest.cc b/net/dns/context_host_resolver_unittest.cc
index 7d1af6f5..ff4467b7 100644
--- a/net/dns/context_host_resolver_unittest.cc
+++ b/net/dns/context_host_resolver_unittest.cc
@@ -300,8 +300,10 @@
                      false /* delay */);
   SetMockDnsRules(std::move(rules));
 
+  URLRequestContext context;
   auto resolver = std::make_unique<ContextHostResolver>(
       manager_.get(), nullptr /* host_cache */);
+  resolver->SetRequestContext(&context);
   std::unique_ptr<HostResolver::ResolveHostRequest> request =
       resolver->CreateRequest(HostPortPair("example.com", 100),
                               NetLogWithSource(), base::nullopt);
@@ -330,8 +332,10 @@
                      false /* delay */);
   SetMockDnsRules(std::move(rules));
 
+  URLRequestContext context;
   auto resolver = std::make_unique<ContextHostResolver>(
       manager_.get(), nullptr /* host_cache */);
+  resolver->SetRequestContext(&context);
   std::unique_ptr<HostResolver::ResolveHostRequest> request =
       resolver->CreateRequest(HostPortPair("example.com", 100),
                               NetLogWithSource(), base::nullopt);
@@ -348,8 +352,10 @@
 }
 
 TEST_F(ContextHostResolverTest, OnShutdown_SubsequentRequests) {
+  URLRequestContext context;
   auto resolver = std::make_unique<ContextHostResolver>(
       manager_.get(), nullptr /* host_cache */);
+  resolver->SetRequestContext(&context);
   resolver->OnShutdown();
 
   std::unique_ptr<HostResolver::ResolveHostRequest> request1 =
@@ -382,8 +388,10 @@
                      MockDnsClientRule::Result(MockDnsClientRule::EMPTY),
                      false /* delay */);
 
+  URLRequestContext context;
   auto resolver = std::make_unique<ContextHostResolver>(
       manager_.get(), nullptr /* host_cache */);
+  resolver->SetRequestContext(&context);
   std::unique_ptr<HostResolver::ResolveHostRequest> request =
       resolver->CreateRequest(HostPortPair("example.com", 100),
                               NetLogWithSource(), base::nullopt);
diff --git a/net/dns/dns_client.cc b/net/dns/dns_client.cc
index 9589c50c..2718feb 100644
--- a/net/dns/dns_client.cc
+++ b/net/dns/dns_client.cc
@@ -169,6 +169,14 @@
     url_request_context_for_probes_ = url_request_context;
   }
 
+  void CancelProbesForContext(URLRequestContext* url_request_context) override {
+    if (url_request_context_for_probes_ != url_request_context || !factory_)
+      return;
+
+    factory_->CancelDohProbes();
+    url_request_context_for_probes_ = nullptr;
+  }
+
   DnsTransactionFactory* GetTransactionFactory() override {
     return session_.get() ? factory_.get() : nullptr;
   }
@@ -267,6 +275,9 @@
   }
 
   void StartDohProbes(bool network_change) {
+    if (!url_request_context_for_probes_)
+      return;
+
     if (probes_allowed_) {
       factory_->StartDohProbes(url_request_context_for_probes_, network_change);
     } else {
diff --git a/net/dns/dns_client.h b/net/dns/dns_client.h
index 8e71e852..8298ada 100644
--- a/net/dns/dns_client.h
+++ b/net/dns/dns_client.h
@@ -67,6 +67,9 @@
   virtual void SetRequestContextForProbes(
       URLRequestContext* url_request_context) = 0;
 
+  virtual void CancelProbesForContext(
+      URLRequestContext* url_request_context) = 0;
+
   // Returns null if the current config is not valid.
   virtual DnsTransactionFactory* GetTransactionFactory() = 0;
 
diff --git a/net/dns/dns_test_util.cc b/net/dns/dns_test_util.cc
index 0026255..0802e90 100644
--- a/net/dns/dns_test_util.cc
+++ b/net/dns/dns_test_util.cc
@@ -488,6 +488,8 @@
   void StartDohProbes(URLRequestContext* url_request_context,
                       bool network_change) override {}
 
+  void CancelDohProbes() override {}
+
   DnsConfig::SecureDnsMode GetSecureDnsModeForTest() override {
     return DnsConfig::SecureDnsMode::AUTOMATIC;
   }
@@ -574,6 +576,9 @@
 void MockDnsClient::SetRequestContextForProbes(
     URLRequestContext* url_request_context) {}
 
+void MockDnsClient::CancelProbesForContext(
+    URLRequestContext* url_request_context) {}
+
 DnsTransactionFactory* MockDnsClient::GetTransactionFactory() {
   return GetEffectiveConfig() ? factory_.get() : nullptr;
 }
diff --git a/net/dns/dns_test_util.h b/net/dns/dns_test_util.h
index 7b5e751..653c7e14 100644
--- a/net/dns/dns_test_util.h
+++ b/net/dns/dns_test_util.h
@@ -277,6 +277,7 @@
   const DnsHosts* GetHosts() const override;
   void SetRequestContextForProbes(
       URLRequestContext* url_request_context) override;
+  void CancelProbesForContext(URLRequestContext* url_request_context) override;
   DnsTransactionFactory* GetTransactionFactory() override;
   AddressSorter* GetAddressSorter() override;
   void IncrementInsecureFallbackFailures() override;
diff --git a/net/dns/dns_transaction.cc b/net/dns/dns_transaction.cc
index 2ec718b..be99840 100644
--- a/net/dns/dns_transaction.cc
+++ b/net/dns/dns_transaction.cc
@@ -555,6 +555,8 @@
                              std::vector<std::unique_ptr<DnsAttempt>>* attempts,
                              URLRequestContext* url_request_context,
                              RequestPriority request_priority) {
+  DCHECK(url_request_context);
+
   uint16_t id = session->NextQueryId();
   std::unique_ptr<DnsQuery> query;
   if (attempts->empty()) {
@@ -860,6 +862,8 @@
   void StartProbe(int doh_server_index,
                   URLRequestContext* context,
                   bool network_change) {
+    DCHECK(context);
+
     // Clear the existing probe stats.
     probe_stats_[doh_server_index] = std::make_unique<ProbeStats>();
     session_->SetProbeSuccess(doh_server_index, false /* success */);
@@ -869,6 +873,8 @@
                   base::TimeTicks::Now() /* sequence_start_time */);
   }
 
+  void CancelProbes() { probe_stats_.clear(); }
+
  private:
   struct ProbeStats {
     ProbeStats()
@@ -896,6 +902,7 @@
     // than on probe completion.
     DCHECK(probe_stats);
     DCHECK(probe_stats->backoff_entry);
+    DCHECK(context);
     probe_stats->backoff_entry->InformOfRequest(false /* success */);
     base::SequencedTaskRunnerHandle::Get()->PostDelayedTask(
         FROM_HERE,
@@ -1188,6 +1195,8 @@
 
   AttemptResult MakeHTTPAttempt() {
     DCHECK(secure_);
+    DCHECK(url_request_context_);
+
     // doh_attempts_ counts the number of attempts made via HTTPS. To
     // get a server index cap that by the number of DoH servers we
     // have configured and search for the next good server.
@@ -1476,12 +1485,19 @@
 
   void StartDohProbes(URLRequestContext* context,
                       bool network_change) override {
+    if (!context) {
+      // Unable to run DoH probes without a URLRequestContext.
+      return;
+    }
+
     for (size_t i = 0; i < session_->config().dns_over_https_servers.size();
          i++) {
       probe_runner_->StartProbe(i, context, network_change);
     }
   }
 
+  void CancelDohProbes() override { probe_runner_->CancelProbes(); }
+
   DnsConfig::SecureDnsMode GetSecureDnsModeForTest() override {
     return session_->config().secure_dns_mode;
   }
diff --git a/net/dns/dns_transaction.h b/net/dns/dns_transaction.h
index 62a4773..0a327b7 100644
--- a/net/dns/dns_transaction.h
+++ b/net/dns/dns_transaction.h
@@ -96,6 +96,8 @@
   virtual void StartDohProbes(URLRequestContext* context,
                               bool network_change) = 0;
 
+  virtual void CancelDohProbes() = 0;
+
   // Returns the default SecureDnsMode in the config.
   virtual DnsConfig::SecureDnsMode GetSecureDnsModeForTest() = 0;
 
diff --git a/net/dns/dns_transaction_unittest.cc b/net/dns/dns_transaction_unittest.cc
index 52a5593..35a65bf 100644
--- a/net/dns/dns_transaction_unittest.cc
+++ b/net/dns/dns_transaction_unittest.cc
@@ -835,6 +835,9 @@
     for (size_t i = 0; i < socket_data_.size(); ++i) {
       EXPECT_TRUE(socket_data_[i]->GetProvider()->AllWriteDataConsumed()) << i;
     }
+
+    URLRequestFilter* filter = URLRequestFilter::GetInstance();
+    filter->ClearHandlers();
   }
 
  protected:
@@ -864,11 +867,6 @@
  public:
   DnsTransactionTest() = default;
   ~DnsTransactionTest() override = default;
-
-  void TearDown() override {
-    URLRequestFilter* filter = URLRequestFilter::GetInstance();
-    filter->ClearHandlers();
-  }
 };
 
 class DnsTransactionTestWithMockTime : public DnsTransactionTestBase,
@@ -2260,10 +2258,6 @@
   AddSocketData(std::move(data));
   // Second attempt gets NXDOMAIN, which happens before the TCP required.
   AddSyncQueryAndRcode(kT0HostName, kT0Qtype, dns_protocol::kRcodeNXDOMAIN);
-  std::unique_ptr<DnsSocketData> tcp_data(new DnsSocketData(
-      0 /* id */, kT0HostName, kT0Qtype, ASYNC, Transport::TCP));
-  tcp_data->AddReadError(ERR_CONNECTION_CLOSED, SYNCHRONOUS);
-  AddSocketData(std::move(tcp_data));
 
   TransactionHelper helper0(kT0HostName, kT0Qtype, false /* secure */,
                             ERR_NAME_NOT_RESOLVED);
@@ -2285,10 +2279,6 @@
   // required.
   AddSyncQueryAndResponse(0 /* id */, kT0HostName, kT0Qtype,
                           kT0ResponseDatagram, base::size(kT0ResponseDatagram));
-  std::unique_ptr<DnsSocketData> tcp_data(new DnsSocketData(
-      0 /* id */, kT0HostName, kT0Qtype, ASYNC, Transport::TCP));
-  tcp_data->AddReadError(ERR_CONNECTION_CLOSED, SYNCHRONOUS);
-  AddSocketData(std::move(tcp_data));
 
   TransactionHelper helper0(kT0HostName, kT0Qtype, false /* secure */,
                             kT0RecordCount);
@@ -2386,6 +2376,68 @@
       0);
 }
 
+TEST_F(DnsTransactionTestWithMockTime, CancelDohProbes) {
+  ConfigureDohServers(true /* use_post */);
+  TestURLRequestContext request_context;
+  AddQueryAndErrorResponse(4, kT4HostName, kT4Qtype, ERR_CONNECTION_REFUSED,
+                           SYNCHRONOUS, Transport::HTTPS,
+                           nullptr /* opt_rdata */,
+                           DnsQuery::PaddingStrategy::BLOCK_LENGTH_128);
+  AddQueryAndErrorResponse(4, kT4HostName, kT4Qtype, ERR_CONNECTION_REFUSED,
+                           SYNCHRONOUS, Transport::HTTPS,
+                           nullptr /* opt_rdata */,
+                           DnsQuery::PaddingStrategy::BLOCK_LENGTH_128);
+  transaction_factory_->StartDohProbes(&request_context,
+                                       false /* network_change */);
+
+  // The first probe happens without any delay.
+  RunUntilIdle();
+  EXPECT_EQ(
+      session_->NextGoodDohServerIndex(0, DnsConfig::SecureDnsMode::AUTOMATIC),
+      -1);
+
+  // Expect the server to still be unavailable after the second probe.
+  FastForwardBy(transaction_factory_->GetDelayUntilNextProbeForTest(0));
+  EXPECT_EQ(
+      session_->NextGoodDohServerIndex(0, DnsConfig::SecureDnsMode::AUTOMATIC),
+      -1);
+
+  transaction_factory_->CancelDohProbes();
+
+  // Server stays unavailable because probe canceled before (non-existent)
+  // success. No success result is added, so this FastForward will cause a
+  // failure if probes attempt to run.
+  FastForwardBy(transaction_factory_->GetDelayUntilNextProbeForTest(0));
+  EXPECT_EQ(
+      session_->NextGoodDohServerIndex(0, DnsConfig::SecureDnsMode::AUTOMATIC),
+      -1);
+}
+
+TEST_F(DnsTransactionTestWithMockTime, CancelDohProbes_AfterSuccess) {
+  ConfigureDohServers(true /* use_post */);
+  TestURLRequestContext request_context;
+  AddQueryAndResponse(4, kT4HostName, kT4Qtype, kT4ResponseDatagram,
+                      base::size(kT4ResponseDatagram), SYNCHRONOUS,
+                      Transport::HTTPS, nullptr /* opt_rdata */,
+                      DnsQuery::PaddingStrategy::BLOCK_LENGTH_128);
+  transaction_factory_->StartDohProbes(&request_context,
+                                       false /* network_change */);
+
+  // The first probe happens without any delay, and immediately succeeds.
+  RunUntilIdle();
+  EXPECT_EQ(
+      session_->NextGoodDohServerIndex(0, DnsConfig::SecureDnsMode::AUTOMATIC),
+      0);
+
+  transaction_factory_->CancelDohProbes();
+
+  // No change expected after cancellation.
+  RunUntilIdle();
+  EXPECT_EQ(
+      session_->NextGoodDohServerIndex(0, DnsConfig::SecureDnsMode::AUTOMATIC),
+      0);
+}
+
 }  // namespace
 
 }  // namespace net
diff --git a/net/dns/host_resolver_manager.cc b/net/dns/host_resolver_manager.cc
index ee9b0b3..b233cc61 100644
--- a/net/dns/host_resolver_manager.cc
+++ b/net/dns/host_resolver_manager.cc
@@ -2542,6 +2542,17 @@
   dns_client_->SetRequestContextForProbes(url_request_context);
 }
 
+void HostResolverManager::CancelProbesForContext(
+    URLRequestContext* url_request_context) {
+  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
+
+  // If no DnsClient, there are no probes to cancel.
+  if (!dns_client_)
+    return;
+
+  dns_client_->CancelProbesForContext(url_request_context);
+}
+
 void HostResolverManager::AddHostCacheInvalidator(
     HostCache::Invalidator* invalidator) {
   host_cache_invalidators_.AddObserver(invalidator);
diff --git a/net/dns/host_resolver_manager.h b/net/dns/host_resolver_manager.h
index 988203b..7d8f611 100644
--- a/net/dns/host_resolver_manager.h
+++ b/net/dns/host_resolver_manager.h
@@ -154,8 +154,15 @@
   void SetDnsConfigOverrides(DnsConfigOverrides overrides);
 
   // Sets the URLRequestContext to use for issuing DoH probes.
+  // TODO(crbug.com/1006902): Convert DoH probes to an API more consistent with
+  // normal requests with a Request or cancellation handle to control start and
+  // cancel.
   void SetRequestContextForProbes(URLRequestContext* url_request_context);
 
+  // Iff |url_request_context| is being used for DoH probes, cancels the probes
+  // and clears the set context.
+  void CancelProbesForContext(URLRequestContext* url_request_context);
+
   // Support for invalidating HostCaches on changes to network or DNS
   // configuration. HostCaches should register/deregister invalidators here
   // rather than attempting to listen for relevant network change signals
diff --git a/net/docs/net-log.md b/net/docs/net-log.md
index 7dea1cf..4097bc8 100644
--- a/net/docs/net-log.md
+++ b/net/docs/net-log.md
@@ -73,3 +73,105 @@
 viewer](https://chromium.googlesource.com/catapult/+/master/netlog_viewer/) for
 instance pretty prints certain parameters based on their names, and the event
 name that added them.
+
+### Example 1
+
+Add an `PROXY_RESOLUTION_SERVICE` event without any parameters, at all capture
+modes.
+
+```
+net_log.BeginEvent(NetLogEventType::PROXY_RESOLUTION_SERVICE);
+```
+
+Analysis:
+
+* Privacy: Logging the event at all capture modes only reveals timing
+  information.
+* Performance: When not logging, has the overhead of an unconditional function
+  call (`BeginEvent`), and then a branch (test on `IsCapturing()`).
+* Size: Minimal data added to NetLog - just one parameterless event per URL
+  request.
+
+### Example 2
+
+Add a `FTP_COMMAND_SENT` event, at all capture modes, along with parameters
+that describe the FTP command.
+
+```
+if (net_log.IsCapturing()) {
+  std::string command = BuildCommandForLog();
+  net_log.AddEventWithStringParams(NetLogEventType::FTP_COMMAND_SENT,
+                                   "command", command);
+}
+```
+
+Analysis:
+
+* Privacy: Low risk given FTP traffic is unencrypted. `BuildCommandForString()`
+  should additionally best-effort strip any identity information, as this is
+  being logged at all capture modes.
+* Performance: Costs one branch when not capturing. The call to
+  `BuildCommandForString()` is only executed when capturing.
+* Size: Cost is proportional to the average FTP command length and frequency of
+  FTP, both of which are low. `BuildCommandForLog()` needn't strictly bound the
+  string length. If a huge FTP command makes it to a NetLog, there is a good
+  chance that is the problem being debugged.
+
+### Example 3
+
+Add a `SSL_CERTIFICATES_RECEIVED` event, along with the full certificate chain,
+at all capture modes.
+
+```
+net_log.AddEvent(NetLogEventType::SSL_CERTIFICATES_RECEIVED, [&] {
+  base::Value dict(base::Value::Type::DICTIONARY);
+  base::Value certs(base::Value::Type::LIST);
+  std::vector<std::string> encoded_chain;
+  server_cert_->GetPEMEncodedChain(&encoded_chain);
+  for (auto& pem : encoded_chain)
+    certs.Append(std::move(pem));
+  dict.SetKey("certificates", std::move(certs));
+  return dict;
+});
+```
+
+Analysis:
+
+* Privacy: Low risk as server certificates are generally public data.
+* Performance: Costs one branch when logging is off (hidden by template
+  expansion). The code in the lambda which builds the `base::Value` parameters is only
+  executed when capturing.
+* Size: On average 8K worth of data per request (average of 2K/certificate,
+  chain length of 3, and the overhead of PEM-encoding). This is heavy-weight
+  for inclusion at `kDefault` capture mode, however justified based on how
+  useful the data is.
+
+### Example 4
+
+Add a `COOKIE_STORE_COOKIE_ADDED` event at all capture modes. Moreover, if the
+capture mode is `kIncludeSensitive` or `kEverything`, also logs the cookie's
+name and value.
+
+```
+net_log.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
+                 [&](NetLogCaptureMode capture_mode) {
+                   if (!NetLogCaptureIncludesSensitive(capture_mode))
+                     return base::Value();
+                   base::Value dict(base::Value::Type::DICTIONARY);
+                   dict.SetStringKey("name", cookie->Name());
+                   dict.SetStringKey("value", cookie->Value());
+                   return dict;
+                 });
+```
+
+Analysis:
+
+* Privacy: The cookie name and value are not included at the `kDefault` capture
+  mode, so only cookie counts and timing information is revealed.
+* Performance: Costs one branch when logging is off (hidden by template
+  expansion). The code in the lambda which builds the `base::Value` parameters is only
+  executed when capturing.
+* Size: For default captured logs, has a file size cost proportional to the
+  number of cookies added. This is borderline justifiable. It would be better
+  in this case to simply omit the event all together at `kDefault` than to log
+  a parameterless event, as the parameterless event is not broadly useful.
diff --git a/net/http/http_cache_lookup_manager_unittest.cc b/net/http/http_cache_lookup_manager_unittest.cc
index 35f1dbbc..a7a3bd4 100644
--- a/net/http/http_cache_lookup_manager_unittest.cc
+++ b/net/http/http_cache_lookup_manager_unittest.cc
@@ -5,8 +5,10 @@
 #include <memory>
 #include <string>
 
+#include "base/feature_list.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
+#include "net/base/features.h"
 #include "net/base/net_errors.h"
 #include "net/base/test_completion_callback.h"
 #include "net/http/http_cache_lookup_manager.h"
@@ -135,6 +137,13 @@
 }
 
 TEST(HttpCacheLookupManagerTest, ServerPushHitCache) {
+  // Skip test if split cache is enabled, as it breaks push.
+  // crbug.com/1009619
+  if (base::FeatureList::IsEnabled(
+          net::features::kSplitCacheByNetworkIsolationKey)) {
+    return;
+  }
+
   base::test::TaskEnvironment task_environment;
   MockHttpCache mock_cache;
   HttpCacheLookupManager push_delegate(mock_cache.http_cache());
@@ -173,6 +182,13 @@
 // pending lookup transaction for the same URL, the new server push will not
 // send a new lookup transaction and should not be canceled.
 TEST(HttpCacheLookupManagerTest, ServerPushPendingLookup) {
+  // Skip test if split cache is enabled, as it breaks push.
+  // crbug.com/1009619
+  if (base::FeatureList::IsEnabled(
+          net::features::kSplitCacheByNetworkIsolationKey)) {
+    return;
+  }
+
   base::test::TaskEnvironment task_environment;
   MockHttpCache mock_cache;
   HttpCacheLookupManager push_delegate(mock_cache.http_cache());
@@ -218,6 +234,13 @@
 
 // Test the server push lookup is based on the full url.
 TEST(HttpCacheLookupManagerTest, ServerPushLookupOnUrl) {
+  // Skip test if split cache is enabled, as it breaks push.
+  // crbug.com/1009619
+  if (base::FeatureList::IsEnabled(
+          net::features::kSplitCacheByNetworkIsolationKey)) {
+    return;
+  }
+
   base::test::TaskEnvironment task_environment;
   MockHttpCache mock_cache;
   HttpCacheLookupManager push_delegate(mock_cache.http_cache());
diff --git a/printing/print_job_constants.cc b/printing/print_job_constants.cc
index 96ca41a..8476582 100644
--- a/printing/print_job_constants.cc
+++ b/printing/print_job_constants.cc
@@ -21,9 +21,6 @@
 // Print using cloud print: true if selected, false if not.
 const char kSettingCloudPrintId[] = "cloudPrintID";
 
-// Print using cloud print dialog: true if selected, false if not.
-const char kSettingCloudPrintDialog[] = "printWithCloudPrint";
-
 // Print job setting 'collate'.
 const char kSettingCollate[] = "collate";
 
@@ -162,29 +159,21 @@
 const char kSettingPrintableAreaWidth[] = "printableAreaWidth";
 const char kSettingPrintableAreaHeight[] = "printableAreaHeight";
 
-// Printer name.
-const char kSettingPrinterName[] = "printerName";
-
 // Printer description.
 const char kSettingPrinterDescription[] = "printerDescription";
 
+// Printer name.
+const char kSettingPrinterName[] = "printerName";
+
 // Additional printer options.
 const char kSettingPrinterOptions[] = "printerOptions";
 
+// The printer type is an enum PrinterType.
+const char kSettingPrinterType[] = "printerType";
+
 // Print to Google Drive option: true if selected, false if not.
 const char kSettingPrintToGoogleDrive[] = "printToGoogleDrive";
 
-// Print to PDF option: true if selected, false if not.
-const char kSettingPrintToPDF[] = "printToPDF";
-
-// Print using Privet option: true if destination is a Privet printer, false if
-// not.
-const char kSettingPrintWithPrivet[] = "printWithPrivet";
-
-// Print using extension option: true if destination is an extension printer,
-// false if not.
-const char kSettingPrintWithExtension[] = "printWithExtension";
-
 // Scaling factor
 const char kSettingScaleFactor[] = "scaleFactor";
 
diff --git a/printing/print_job_constants.h b/printing/print_job_constants.h
index e13c021..273b76a 100644
--- a/printing/print_job_constants.h
+++ b/printing/print_job_constants.h
@@ -14,7 +14,6 @@
 PRINTING_EXPORT extern const char kPreviewUIID[];
 PRINTING_EXPORT extern const char kSettingCapabilities[];
 PRINTING_EXPORT extern const char kSettingCloudPrintId[];
-PRINTING_EXPORT extern const char kSettingCloudPrintDialog[];
 PRINTING_EXPORT extern const char kSettingCollate[];
 PRINTING_EXPORT extern const char kSettingColor[];
 PRINTING_EXPORT extern const char kSettingSetColorAsDefault[];
@@ -59,9 +58,6 @@
 PRINTING_EXPORT extern const char kSettingPreviewIsPdf[];
 PRINTING_EXPORT extern const char kSettingPreviewModifiable[];
 PRINTING_EXPORT extern const char kSettingPrintToGoogleDrive[];
-PRINTING_EXPORT extern const char kSettingPrintToPDF[];
-PRINTING_EXPORT extern const char kSettingPrintWithPrivet[];
-PRINTING_EXPORT extern const char kSettingPrintWithExtension[];
 PRINTING_EXPORT extern const char kSettingPrintableAreaHeight[];
 PRINTING_EXPORT extern const char kSettingPrintableAreaWidth[];
 PRINTING_EXPORT extern const char kSettingPrintableAreaX[];
@@ -69,6 +65,7 @@
 PRINTING_EXPORT extern const char kSettingPrinterDescription[];
 PRINTING_EXPORT extern const char kSettingPrinterName[];
 PRINTING_EXPORT extern const char kSettingPrinterOptions[];
+PRINTING_EXPORT extern const char kSettingPrinterType[];
 PRINTING_EXPORT extern const char kSettingRasterizePdf[];
 PRINTING_EXPORT extern const char kSettingScaleFactor[];
 PRINTING_EXPORT extern const char kSettingTicket[];
@@ -156,6 +153,16 @@
   MARGIN_TYPE_LAST = CUSTOM_MARGINS
 };
 
+// Must match print_preview.PrinterType in
+// chrome/browser/resources/print_preview/data/destination_match.js
+enum PrinterType {
+  kPrivetPrinter,
+  kExtensionPrinter,
+  kPdfPrinter,
+  kLocalPrinter,
+  kCloudPrinter
+};
+
 }  // namespace printing
 
 #endif  // PRINTING_PRINT_JOB_CONSTANTS_H_
diff --git a/printing/printing_context.cc b/printing/printing_context.cc
index 7a7d36b..29d9c4e 100644
--- a/printing/printing_context.cc
+++ b/printing/printing_context.cc
@@ -81,10 +81,7 @@
   pdf_settings.SetIntKey(kSettingDuplexMode, printing::SIMPLEX);
   pdf_settings.SetBoolKey(kSettingLandscape, false);
   pdf_settings.SetStringKey(kSettingDeviceName, "");
-  pdf_settings.SetBoolKey(kSettingPrintToPDF, true);
-  pdf_settings.SetBoolKey(kSettingCloudPrintDialog, false);
-  pdf_settings.SetBoolKey(kSettingPrintWithPrivet, false);
-  pdf_settings.SetBoolKey(kSettingPrintWithExtension, false);
+  pdf_settings.SetIntKey(kSettingPrinterType, kPdfPrinter);
   pdf_settings.SetIntKey(kSettingScaleFactor, 100);
   pdf_settings.SetBoolKey(kSettingRasterizePdf, false);
   pdf_settings.SetIntKey(kSettingPagesPerSheet, 1);
@@ -100,33 +97,22 @@
     return OnError();
   }
 
-  base::Optional<bool> print_to_pdf_opt =
-      job_settings.FindBoolKey(kSettingPrintToPDF);
-  base::Optional<bool> is_cloud_dialog_opt =
-      job_settings.FindBoolKey(kSettingCloudPrintDialog);
-  base::Optional<bool> print_with_privet_opt =
-      job_settings.FindBoolKey(kSettingPrintWithPrivet);
-  base::Optional<bool> print_with_extension_opt =
-      job_settings.FindBoolKey(kSettingPrintWithExtension);
-
-  if (!print_to_pdf_opt || !is_cloud_dialog_opt || !print_with_privet_opt ||
-      !print_with_extension_opt) {
+  base::Optional<int> printer_type_opt =
+      job_settings.FindIntKey(kSettingPrinterType);
+  if (!printer_type_opt.has_value()) {
     NOTREACHED();
     return OnError();
   }
 
-  bool print_to_pdf = print_to_pdf_opt.value();
-  bool is_cloud_dialog = is_cloud_dialog_opt.value();
-  bool print_with_privet = print_with_privet_opt.value();
-  bool print_with_extension = print_with_extension_opt.value();
-
-  bool print_to_cloud = job_settings.FindKey(kSettingCloudPrintId) != nullptr;
+  PrinterType printer_type = static_cast<PrinterType>(printer_type_opt.value());
+  bool print_with_privet = printer_type == kPrivetPrinter;
+  bool print_to_cloud = !!job_settings.FindKey(kSettingCloudPrintId);
   bool open_in_external_preview =
-      job_settings.FindKey(kSettingOpenPDFInPreview) != nullptr;
+      !!job_settings.FindKey(kSettingOpenPDFInPreview);
 
   if (!open_in_external_preview &&
-      (print_to_pdf || print_to_cloud || is_cloud_dialog || print_with_privet ||
-       print_with_extension)) {
+      (print_to_cloud || print_with_privet || printer_type == kPdfPrinter ||
+       printer_type == kCloudPrinter || printer_type == kExtensionPrinter)) {
     settings_->set_dpi(kDefaultPdfDpi);
     gfx::Size paper_size(GetPdfPaperSizeDeviceUnits());
     if (!settings_->requested_media().size_microns.IsEmpty()) {
diff --git a/remoting/base/BUILD.gn b/remoting/base/BUILD.gn
index c60d2a4..a8e5b1f 100644
--- a/remoting/base/BUILD.gn
+++ b/remoting/base/BUILD.gn
@@ -25,8 +25,6 @@
     "name_value_map.h",
     "rate_counter.cc",
     "rate_counter.h",
-    "remoting_bot.cc",
-    "remoting_bot.h",
     "result.h",
     "rsa_key_pair.cc",
     "rsa_key_pair.h",
diff --git a/remoting/base/remoting_bot.cc b/remoting/base/remoting_bot.cc
deleted file mode 100644
index f1782073..0000000
--- a/remoting/base/remoting_bot.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "remoting/base/remoting_bot.h"
-
-namespace remoting {
-
-bool IsValidBotJid(const std::string& input) {
-  if (input == kRemotingBotJid) {
-    return true;
-  }
-#if !defined(NDEBUG)
-  if (input == kRemotingTestBotJid) {
-    return true;
-  }
-#endif  // !defined(NDEBUG)
-  return false;
-}
-
-}  // namespace remoting
\ No newline at end of file
diff --git a/remoting/base/remoting_bot.h b/remoting/base/remoting_bot.h
deleted file mode 100644
index 7fdef31..0000000
--- a/remoting/base/remoting_bot.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright 2017 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef REMOTING_BASE_REMOTING_BOT_H_
-#define REMOTING_BASE_REMOTING_BOT_H_
-
-#include <string>
-
-namespace remoting {
-
-// The JID of the remoting bot.
-const char kRemotingBotJid[] = "remoting@bot.talk.google.com";
-
-#if !defined(NDEBUG)
-// The JID of the remoting bot test instance.
-const char kRemotingTestBotJid[] = "remoting-test@bot.talk.google.com";
-#endif  // !defined(NDEBUG)
-
-// Returns true if |input| is a valid bot JID.
-bool IsValidBotJid(const std::string& input);
-
-}  // namespace remoting
-
-#endif  // REMOTING_BASE_REMOTING_BOT_H_
\ No newline at end of file
diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc
index 31e738a..f1f0711 100644
--- a/remoting/client/chromoting_client.cc
+++ b/remoting/client/chromoting_client.cc
@@ -244,7 +244,7 @@
   DCHECK(thread_checker_.CalledOnValidThread());
 
   if (state == SignalStrategy::CONNECTED) {
-    VLOG(1) << "Connected as: " << signal_strategy_->GetLocalAddress().jid();
+    VLOG(1) << "Connected as: " << signal_strategy_->GetLocalAddress().id();
     // After signaling has been connected we can try connecting to the host.
     if (connection_ &&
         connection_->state() == protocol::ConnectionToHost::INITIALIZING) {
diff --git a/remoting/host/ftl_signaling_connector.cc b/remoting/host/ftl_signaling_connector.cc
index 875c18c..2c17a527 100644
--- a/remoting/host/ftl_signaling_connector.cc
+++ b/remoting/host/ftl_signaling_connector.cc
@@ -92,7 +92,7 @@
 
   if (state == SignalStrategy::CONNECTED) {
     HOST_LOG << "Signaling connected. New JID: "
-             << signal_strategy_->GetLocalAddress().jid();
+             << signal_strategy_->GetLocalAddress().id();
     backoff_reset_timer_.Start(FROM_HERE, kBackoffResetDelay, &backoff_,
                                &net::BackoffEntry::Reset);
   } else if (state == SignalStrategy::DISCONNECTED) {
diff --git a/remoting/host/gcd_state_updater.cc b/remoting/host/gcd_state_updater.cc
index 3fea900..15e74e8b 100644
--- a/remoting/host/gcd_state_updater.cc
+++ b/remoting/host/gcd_state_updater.cc
@@ -81,7 +81,7 @@
   }
 
   if (result == GcdRestClient::NETWORK_ERROR ||
-      pending_request_jid_ != signal_strategy_->GetLocalAddress().jid()) {
+      pending_request_jid_ != signal_strategy_->GetLocalAddress().id()) {
     // Continue exponential backoff.
     return;
   }
@@ -117,7 +117,7 @@
   // Construct an update to the remote state.
   std::unique_ptr<base::DictionaryValue> patch(new base::DictionaryValue);
   std::unique_ptr<base::DictionaryValue> base_state(new base::DictionaryValue);
-  pending_request_jid_ = signal_strategy_->GetLocalAddress().jid();
+  pending_request_jid_ = signal_strategy_->GetLocalAddress().id();
   base_state->SetString("_jabberId", pending_request_jid_);
   base_state->SetString("_hostVersion", STRINGIZE(VERSION));
   patch->Set("base", std::move(base_state));
diff --git a/remoting/host/heartbeat_sender.cc b/remoting/host/heartbeat_sender.cc
index c8f4d54..fe825fd 100644
--- a/remoting/host/heartbeat_sender.cc
+++ b/remoting/host/heartbeat_sender.cc
@@ -324,7 +324,7 @@
 
   apis::v1::HeartbeatRequest heartbeat;
   std::string signaling_id;
-  heartbeat.set_tachyon_id(signal_strategy_->GetLocalAddress().jid());
+  heartbeat.set_tachyon_id(signal_strategy_->GetLocalAddress().id());
   heartbeat.set_host_id(host_id_);
   if (!host_offline_reason_.empty()) {
     heartbeat.set_host_offline_reason(host_offline_reason_);
diff --git a/remoting/host/remoting_register_support_host_request.cc b/remoting/host/remoting_register_support_host_request.cc
index 5afa5e5..02e8aacc 100644
--- a/remoting/host/remoting_register_support_host_request.cc
+++ b/remoting/host/remoting_register_support_host_request.cc
@@ -136,7 +136,7 @@
 
   apis::v1::RegisterSupportHostRequest request;
   request.set_public_key(key_pair_->GetPublicKey());
-  request.set_tachyon_id(signal_strategy_->GetLocalAddress().jid());
+  request.set_tachyon_id(signal_strategy_->GetLocalAddress().id());
   request.set_host_version(STRINGIZE(VERSION));
   request.set_host_os_name(GetHostOperatingSystemName());
   request.set_host_os_version(GetHostOperatingSystemVersion());
diff --git a/remoting/protocol/jingle_messages.cc b/remoting/protocol/jingle_messages.cc
index 75526f2f..9367bd0 100644
--- a/remoting/protocol/jingle_messages.cc
+++ b/remoting/protocol/jingle_messages.cc
@@ -8,7 +8,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "remoting/base/constants.h"
 #include "remoting/base/name_value_map.h"
-#include "remoting/base/remoting_bot.h"
 #include "remoting/protocol/content_description.h"
 #include "remoting/protocol/session_plugin.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
diff --git a/remoting/protocol/jingle_messages_unittest.cc b/remoting/protocol/jingle_messages_unittest.cc
index 7bc510c..1f825ef 100644
--- a/remoting/protocol/jingle_messages_unittest.cc
+++ b/remoting/protocol/jingle_messages_unittest.cc
@@ -405,28 +405,6 @@
               jingle_xmpp::QName("urn:xmpp:jingle:1", "test-info"));
 }
 
-TEST(JingleMessageTest, IgnoreInvalidAddress) {
-  const char* kInvalidFromField =
-      "<cli:iq from='evil@gmail.com' "
-              "to='victim@gmail.com/chromiumsy5C6A652D' type='set' "
-              "xmlns:cli='jabber:client'>"
-        "<jingle action='session-info' "
-                "sid='2227053353' xmlns='urn:xmpp:jingle:1' "
-                "from-channel='lcs' "
-                "from-endpoint-id='victim@gmail.com/evilAddress'>"
-          "<test-info>TestMessage</test-info>"
-        "</jingle>"
-      "</cli:iq>";
-
-  std::unique_ptr<XmlElement> xml(XmlElement::ForStr(kInvalidFromField));
-  ASSERT_TRUE(xml.get());
-  EXPECT_TRUE(JingleMessage::IsJingleMessage(xml.get()));
-  JingleMessage message;
-  std::string error;
-  EXPECT_FALSE(message.ParseXml(xml.get(), &error));
-  EXPECT_TRUE(message.from.empty());
-}
-
 TEST(JingleMessageReplyTest, ToXml) {
   const char* kTestIncomingMessage1 =
       "<cli:iq from='user@gmail.com/chromoting016DBB07' id='4' "
@@ -436,9 +414,8 @@
       "</reason></jingle></cli:iq>";
   const char* kTestIncomingMessage2 =
       "<cli:iq from='remoting@bot.talk.google.com' id='4' "
-      "to='user@gmail.com/chromiumsy5C6A652D' type='set' "
+      "to='user@gmail.com/chromoting_ftl_5C6A652D' type='set' "
       "xmlns:cli='jabber:client'><jingle action='session-terminate' "
-      "from-channel='lcs' from-endpoint-id='from@gmail.com/AbCdEf1234=' "
       "sid='2227053353' xmlns='urn:xmpp:jingle:1'><reason><success/>"
       "</reason></jingle></cli:iq>";
 
@@ -494,15 +471,13 @@
        "<iq xmlns='jabber:client' "
        "to='remoting@bot.talk.google.com' id='4' "
        "type='error'><jingle "
-       "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1' "
-       "from-channel='lcs' from-endpoint-id='from@gmail.com/AbCdEf1234='>"
+       "action='session-terminate' sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
        "<reason><success/></reason></jingle><error type='modify'>"
        "<item-not-found/><text xml:lang='en'>ErrorText</text></error></iq>",
        kTestIncomingMessage2},
       {JingleMessageReply::NONE, "",
        "<iq xmlns='jabber:client' to='remoting@bot.talk.google.com' id='4' "
-       "type='result'><jingle xmlns='urn:xmpp:jingle:1' to-channel='lcs' "
-       "to-endpoint-id='from@gmail.com/AbCdEf1234='/></iq>",
+       "type='result'><jingle xmlns='urn:xmpp:jingle:1'/></iq>",
        kTestIncomingMessage2},
   };
 
diff --git a/remoting/protocol/jingle_session.cc b/remoting/protocol/jingle_session.cc
index c48f0b4..9694baf5 100644
--- a/remoting/protocol/jingle_session.cc
+++ b/remoting/protocol/jingle_session.cc
@@ -809,7 +809,7 @@
   std::unique_ptr<JingleMessage> message(new JingleMessage(
       peer_address_, JingleMessage::SESSION_INITIATE, session_id_));
   message->initiator =
-      session_manager_->signal_strategy_->GetLocalAddress().jid();
+      session_manager_->signal_strategy_->GetLocalAddress().id();
   message->description.reset(new ContentDescription(
       session_manager_->protocol_config_->Clone(),
       authenticator_->GetNextMessage()));
diff --git a/remoting/signaling/ftl_signal_strategy.cc b/remoting/signaling/ftl_signal_strategy.cc
index c4693c7e..bceb1292 100644
--- a/remoting/signaling/ftl_signal_strategy.cc
+++ b/remoting/signaling/ftl_signal_strategy.cc
@@ -210,7 +210,7 @@
   DCHECK(to_error.empty());
 
   // Synthesizing the from attribute in the message.
-  stanza->SetAttr(jingle_xmpp::QN_FROM, local_address_.jid());
+  stanza->SetAttr(jingle_xmpp::QN_FROM, local_address_.id());
 
   std::string stanza_id = stanza->Attr(jingle_xmpp::QN_ID);
   SendMessage(to, stanza_id, stanza->Str());
@@ -342,7 +342,7 @@
   bool get_info_result =
       receiver.GetFtlInfo(&receiver_username, &receiver_registration_id);
   if (!get_info_result) {
-    LOG(DFATAL) << "Receiver is not in FTL address: " << receiver.jid();
+    LOG(DFATAL) << "Receiver is not in FTL address: " << receiver.id();
     return;
   }
 
@@ -380,8 +380,8 @@
   auto error_iq = std::make_unique<jingle_xmpp::XmlElement>(jingle_xmpp::QN_IQ);
   error_iq->SetAttr(jingle_xmpp::QN_TYPE, jingle_xmpp::STR_ERROR);
   error_iq->SetAttr(jingle_xmpp::QN_ID, stanza_id);
-  error_iq->SetAttr(jingle_xmpp::QN_FROM, receiver.jid());
-  error_iq->SetAttr(jingle_xmpp::QN_TO, local_address_.jid());
+  error_iq->SetAttr(jingle_xmpp::QN_FROM, receiver.id());
+  error_iq->SetAttr(jingle_xmpp::QN_TO, local_address_.id());
   OnStanza(receiver, std::move(error_iq));
 }
 
@@ -412,12 +412,12 @@
     return;
   }
   if (SignalingAddress(stanza->Attr(jingle_xmpp::QN_FROM)) != sender_address) {
-    LOG(DFATAL) << "Expected sender: " << sender_address.jid()
+    LOG(DFATAL) << "Expected sender: " << sender_address.id()
                 << ", but received: " << stanza->Attr(jingle_xmpp::QN_FROM);
     return;
   }
   if (SignalingAddress(stanza->Attr(jingle_xmpp::QN_TO)) != local_address_) {
-    LOG(DFATAL) << "Expected receiver: " << local_address_.jid()
+    LOG(DFATAL) << "Expected receiver: " << local_address_.id()
                 << ", but received: " << stanza->Attr(jingle_xmpp::QN_TO);
     return;
   }
diff --git a/remoting/signaling/push_notification_subscriber.cc b/remoting/signaling/push_notification_subscriber.cc
index 9ff44457..c796d14 100644
--- a/remoting/signaling/push_notification_subscriber.cc
+++ b/remoting/signaling/push_notification_subscriber.cc
@@ -56,7 +56,7 @@
           << subscription.channel << ".";
 
   std::string bare_jid;
-  SplitJidResource(signal_strategy_->GetLocalAddress().jid(), &bare_jid,
+  SplitJidResource(signal_strategy_->GetLocalAddress().id(), &bare_jid,
                    nullptr);
 
   // Build a subscription request.
diff --git a/remoting/signaling/signaling_address.cc b/remoting/signaling/signaling_address.cc
index c2435b3..eda2f43 100644
--- a/remoting/signaling/signaling_address.cc
+++ b/remoting/signaling/signaling_address.cc
@@ -10,7 +10,6 @@
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
 #include "remoting/base/name_value_map.h"
-#include "remoting/base/remoting_bot.h"
 #include "remoting/signaling/jid_util.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
 
@@ -18,51 +17,22 @@
 
 namespace {
 
-constexpr char kJingleNamespace[] = "urn:xmpp:jingle:1";
-constexpr char kLcsResourcePrefix[] = "chromoting_lcs_";
-
-// FTL JID format:
+// FTL ID format:
 // user@domain.com/chromoting_ftl_(registration ID)
-// The FTL JID is only used local to the program.
+// The FTL ID is only used local to the program.
 constexpr char kFtlResourcePrefix[] = "chromoting_ftl_";
 
-// Represents the XML attrbute names for the various address fields in the
-// iq stanza.
-enum class Field { JID, CHANNEL, ENDPOINT_ID };
-
-const NameMapElement<SignalingAddress::Channel> kChannelTypes[] = {
-    {SignalingAddress::Channel::LCS, "lcs"},
-    {SignalingAddress::Channel::XMPP, "xmpp"},
-    {SignalingAddress::Channel::FTL, "ftl"},
-};
-
-jingle_xmpp::QName GetQNameByField(Field attr, SignalingAddress::Direction direction) {
-  const char* attribute_name = nullptr;
-  switch (attr) {
-    case Field::JID:
-      attribute_name = (direction == SignalingAddress::FROM) ? "from" : "to";
-      break;
-    case Field::ENDPOINT_ID:
-      attribute_name = (direction == SignalingAddress::FROM)
-                           ? "from-endpoint-id"
-                           : "to-endpoint-id";
-      break;
-    case Field::CHANNEL:
-      attribute_name =
-          (direction == SignalingAddress::FROM) ? "from-channel" : "to-channel";
-      break;
-  }
+jingle_xmpp::QName GetIdQName(SignalingAddress::Direction direction) {
+  const char* attribute_name = attribute_name =
+      (direction == SignalingAddress::FROM) ? "from" : "to";
   return jingle_xmpp::QName("", attribute_name);
 }
 
 SignalingAddress::Channel GetChannelType(std::string address) {
-  std::string bare_jid;
+  std::string email;
   std::string resource;
-  bool has_jid_resource = SplitJidResource(address, &bare_jid, &resource);
-  if (has_jid_resource) {
-    if (resource.find(kLcsResourcePrefix) == 0) {
-      return SignalingAddress::Channel::LCS;
-    }
+  bool has_resource = SplitJidResource(address, &email, &resource);
+  if (has_resource) {
     if (resource.find(kFtlResourcePrefix) == 0) {
       return SignalingAddress::Channel::FTL;
     }
@@ -72,43 +42,24 @@
 
 }  // namespace
 
-SignalingAddress::SignalingAddress()
-    : channel_(SignalingAddress::Channel::XMPP) {}
+SignalingAddress::SignalingAddress() = default;
 
 SignalingAddress::SignalingAddress(const std::string& address) {
+  DCHECK(!address.empty());
   channel_ = GetChannelType(address);
   switch (channel_) {
-    case SignalingAddress::Channel::XMPP:
-      jid_ = NormalizeJid(address);
-      break;
-    case SignalingAddress::Channel::LCS:
-      endpoint_id_ = NormalizeJid(address);
-      jid_ = kRemotingBotJid;
-      break;
-    case SignalingAddress::Channel::FTL:
-      jid_ = NormalizeJid(address);
+    case Channel::XMPP:
+    case Channel::FTL:
+      id_ = NormalizeJid(address);
+      DCHECK(!id_.empty()) << "Missing signaling ID.";
       break;
     default:
       NOTREACHED();
   }
-
-  DCHECK(!jid_.empty()) << "Missing JID.";
-  DCHECK(endpoint_id_.empty() != (channel_ == Channel::LCS))
-      << "EndpointId should be set if and only if the Channel is LCS";
-}
-
-SignalingAddress::SignalingAddress(const std::string& jid,
-                                   const std::string& endpoint_id,
-                                   Channel channel)
-    : jid_(NormalizeJid(jid)),
-      endpoint_id_(NormalizeJid(endpoint_id)),
-      channel_(channel) {
-  DCHECK(!jid.empty());
 }
 
 bool SignalingAddress::operator==(const SignalingAddress& other) const {
-  return (other.jid_ == jid_) && (other.endpoint_id_ == endpoint_id_) &&
-         (other.channel_ == channel_);
+  return (other.id_ == id_) && (other.channel_ == channel_);
 }
 
 bool SignalingAddress::operator!=(const SignalingAddress& other) const {
@@ -128,91 +79,17 @@
 SignalingAddress SignalingAddress::Parse(const jingle_xmpp::XmlElement* iq,
                                          SignalingAddress::Direction direction,
                                          std::string* error) {
-  std::string jid(iq->Attr(GetQNameByField(Field::JID, direction)));
-  if (jid.empty()) {
+  std::string id = iq->Attr(GetIdQName(direction));
+  if (id.empty()) {
     return SignalingAddress();
   }
-
-  const jingle_xmpp::XmlElement* jingle =
-      iq->FirstNamed(jingle_xmpp::QName(kJingleNamespace, "jingle"));
-
-  if (!jingle || GetChannelType(jid) == SignalingAddress::Channel::FTL) {
-    return SignalingAddress(jid);
-  }
-
-  std::string type(iq->Attr(jingle_xmpp::QName(std::string(), "type")));
-  // For error IQs, invert the direction as the jingle node represents the
-  // original request.
-  if (type == "error") {
-    direction = (direction == FROM) ? TO : FROM;
-  }
-
-  std::string endpoint_id(
-      jingle->Attr(GetQNameByField(Field::ENDPOINT_ID, direction)));
-  std::string channel_str(
-      jingle->Attr(GetQNameByField(Field::CHANNEL, direction)));
-  SignalingAddress::Channel channel;
-
-  if (channel_str.empty()) {
-    channel = SignalingAddress::Channel::XMPP;
-  } else if (!NameToValue(kChannelTypes, channel_str, &channel)) {
-    *error = "Unknown channel: " + channel_str;
-    return SignalingAddress();
-  }
-
-  bool is_lcs = (channel == SignalingAddress::Channel::LCS);
-
-  if (is_lcs == endpoint_id.empty()) {
-    *error = (is_lcs ? "Missing |endpoint-id| for LCS channel"
-                     : "|endpoint_id| should be empty for XMPP channel");
-    return SignalingAddress();
-  }
-
-  if (direction == FROM && is_lcs && !IsValidBotJid(jid)) {
-    *error = "Reject LCS message from untrusted sender: " + jid;
-    return SignalingAddress();
-  }
-
-  return SignalingAddress(jid, endpoint_id, channel);
+  return SignalingAddress(id);
 }
 
 void SignalingAddress::SetInMessage(jingle_xmpp::XmlElement* iq,
                                     Direction direction) const {
   DCHECK(!empty()) << "Signaling Address is empty";
-
-  if (direction == FROM) {
-    // The from JID is not used for routing but for sender identification on the
-    // receiving end. Use the ID for specificity.
-    iq->SetAttr(GetQNameByField(Field::JID, direction), id());
-  } else {
-    // The to JID is used for routing and must be a valid XMPP endpoint.  Always
-    // use the XMPP JID.
-    iq->SetAttr(GetQNameByField(Field::JID, direction), jid_);
-  }
-
-  // Do not tamper the routing-info in the jingle tag for error IQ's, as
-  // it corresponds to the original message.
-  std::string type(iq->Attr(jingle_xmpp::QName(std::string(), "type")));
-  if (type == "error") {
-    return;
-  }
-
-  jingle_xmpp::XmlElement* jingle =
-      iq->FirstNamed(jingle_xmpp::QName(kJingleNamespace, "jingle"));
-
-  if (jingle) {
-    // Start from a fresh slate regardless of the previous address format.
-    jingle->ClearAttr(GetQNameByField(Field::CHANNEL, direction));
-    jingle->ClearAttr(GetQNameByField(Field::ENDPOINT_ID, direction));
-
-    // Only set the channel and endpoint_id in the LCS channel.
-    if (channel_ == SignalingAddress::Channel::LCS) {
-      jingle->AddAttr(GetQNameByField(Field::ENDPOINT_ID, direction),
-                      endpoint_id_);
-      jingle->AddAttr(GetQNameByField(Field::CHANNEL, direction),
-                      ValueToName(kChannelTypes, channel_));
-    }
-  }
+  iq->SetAttr(GetIdQName(direction), id_);
 }
 
 bool SignalingAddress::GetFtlInfo(std::string* username,
@@ -221,8 +98,8 @@
     return false;
   }
   std::string resource;
-  bool has_jid_resource = SplitJidResource(jid_, username, &resource);
-  DCHECK(has_jid_resource);
+  bool has_resource = SplitJidResource(id_, username, &resource);
+  DCHECK(has_resource);
   size_t ftl_resource_prefix_length = strlen(kFtlResourcePrefix);
   DCHECK_LT(ftl_resource_prefix_length, resource.length());
   *registration_id = resource.substr(ftl_resource_prefix_length);
diff --git a/remoting/signaling/signaling_address.h b/remoting/signaling/signaling_address.h
index 1508c14e..244658e1 100644
--- a/remoting/signaling/signaling_address.h
+++ b/remoting/signaling/signaling_address.h
@@ -16,15 +16,14 @@
 // Represents an address of a Chromoting endpoint and its routing channel.
 class SignalingAddress {
  public:
-  enum class Channel { LCS, XMPP, FTL };
+  enum class Channel { XMPP, FTL };
   enum Direction { TO, FROM };
   // Creates an empty SignalingAddress.
   SignalingAddress();
 
-  // Creates a SignalingAddress with |jid|, which can either be a valid
-  // XMPP JID, or an LCS address in a JID like format, or FTL address in a JID
-  // like format.
-  explicit SignalingAddress(const std::string& jid);
+  // Creates a SignalingAddress with |id|, which can either be a valid FTL ID,
+  // or XMPP JID.
+  explicit SignalingAddress(const std::string& id);
 
   // Creates a SignalingAddress that represents an FTL endpoint. Note that the
   // FTL SignalingAddress is irrelevant to the FTL server or client. It is just
@@ -37,38 +36,27 @@
                                 Direction direction,
                                 std::string* error);
 
-  void SetInMessage(jingle_xmpp::XmlElement* message, Direction direction) const;
+  void SetInMessage(jingle_xmpp::XmlElement* message,
+                    Direction direction) const;
 
   // Writes FTL info to |username| and |registration_id|. Returns true if the
   // SIgnalingAddress is a valid FTL address and info is successfully written.
   // If this returns false then none of the parameters will be touched.
   bool GetFtlInfo(std::string* username, std::string* registration_id) const;
 
-  const std::string& jid() const { return jid_; }
-  const std::string& endpoint_id() const { return endpoint_id_; }
   Channel channel() const { return channel_; }
-  const std::string& id() const {
-    return (channel_ == Channel::LCS) ? endpoint_id_ : jid_;
-  }
+  const std::string& id() const { return id_; }
 
-  bool empty() const { return jid_.empty(); }
+  bool empty() const { return id_.empty(); }
 
   bool operator==(const SignalingAddress& other) const;
   bool operator!=(const SignalingAddress& other) const;
 
  private:
-  SignalingAddress(const std::string& jid,
-                   const std::string& endpoint_id,
-                   Channel channel);
-
   // Represents the |to| or |from| field in an IQ stanza.
-  std::string jid_;
+  std::string id_;
 
-  // Represents the identifier of an endpoint. In  LCS, this is the LCS address
-  // encoded in a JID like format.  In XMPP, it is empty.
-  std::string endpoint_id_;
-
-  Channel channel_;
+  Channel channel_ = Channel::XMPP;
 };
 
 }  // namespace remoting
diff --git a/remoting/signaling/signaling_address_unittest.cc b/remoting/signaling/signaling_address_unittest.cc
index bd0b1fec..d6c6484 100644
--- a/remoting/signaling/signaling_address_unittest.cc
+++ b/remoting/signaling/signaling_address_unittest.cc
@@ -6,7 +6,6 @@
 
 #include <memory>
 
-#include "remoting/base/remoting_bot.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
 
@@ -34,13 +33,12 @@
 
 TEST(SignalingAddressTest, ParseAddress) {
   const char kTestMessage[] =
-      "<cli:iq from='remoting@bot.talk.google.com' "
-      "      to='user@gmail.com/chromiumsy5C6A652D' type='set' "
+      "<cli:iq"
+      "      from='user@domain.com/chromoting_ftl_sender' "
+      "      to='remoting@bot.talk.google.com' type='set' "
       "      xmlns:cli='jabber:client'>"
       "  <jingle action='session-info' "
-      "        sid='2227053353' xmlns='urn:xmpp:jingle:1' "
-      "        from-channel='lcs' "
-      "        from-endpoint-id='user@gmail.com/xBrnereror='>"
+      "        sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
       "  </jingle>"
       "</cli:iq>";
 
@@ -53,68 +51,26 @@
   EXPECT_FALSE(from.empty());
   EXPECT_TRUE(error.empty());
 
-  EXPECT_EQ("remoting@bot.talk.google.com", from.jid());
-  EXPECT_EQ(SignalingAddress::Channel::LCS, from.channel());
-  EXPECT_EQ("user@gmail.com/xBrnereror=", from.endpoint_id());
-  EXPECT_EQ("user@gmail.com/xBrnereror=", from.id());
+  EXPECT_EQ("user@domain.com/chromoting_ftl_sender", from.id());
+  EXPECT_EQ(SignalingAddress::Channel::FTL, from.channel());
 
   SignalingAddress to =
       SignalingAddress::Parse(message.get(), SignalingAddress::TO, &error);
   EXPECT_FALSE(to.empty());
   EXPECT_TRUE(error.empty());
 
-  EXPECT_EQ("user@gmail.com/chromiumsy5C6A652D", to.jid());
+  EXPECT_EQ("remoting@bot.talk.google.com", to.id());
   EXPECT_EQ(SignalingAddress::Channel::XMPP, to.channel());
-  EXPECT_EQ("", to.endpoint_id());
-  EXPECT_EQ("user@gmail.com/chromiumsy5C6A652D", to.id());
 }
 
-TEST(SignalingAddressTest, ParseErrorAddress) {
+TEST(SignalingAddressTest, ParseEmptySignalingId) {
+  // Parse a message with invalid from field.
   const char kTestMessage[] =
-      "<cli:iq from='remoting@bot.talk.google.com' "
-      "to='user@gmail.com/chromiumsy5C6A652D' type='error' "
+      "<cli:iq from='' "
+      "to='user@gmail.com/chromoting_ftl_receiver' type='result' "
       "      xmlns:cli='jabber:client'>"
       "  <jingle action='session-info' "
-      "        sid='2227053353' xmlns='urn:xmpp:jingle:1' "
-      "        to-channel='lcs' "
-      "        to-endpoint-id='user@gmail.com/xBrnereror='>"
-      "  </jingle>"
-      "<error/>"
-      "</cli:iq>";
-
-  std::unique_ptr<jingle_xmpp::XmlElement> message(
-      jingle_xmpp::XmlElement::ForStr(kTestMessage));
-  std::string error;
-
-  SignalingAddress from =
-      SignalingAddress::Parse(message.get(), SignalingAddress::FROM, &error);
-  EXPECT_FALSE(from.empty());
-
-  EXPECT_EQ("remoting@bot.talk.google.com", from.jid());
-  EXPECT_EQ(SignalingAddress::Channel::LCS, from.channel());
-  EXPECT_EQ("user@gmail.com/xBrnereror=", from.endpoint_id());
-  EXPECT_EQ("user@gmail.com/xBrnereror=", from.id());
-
-  SignalingAddress to =
-      SignalingAddress::Parse(message.get(), SignalingAddress::TO, &error);
-  EXPECT_FALSE(to.empty());
-
-  EXPECT_EQ("user@gmail.com/chromiumsy5C6A652D", to.jid());
-  EXPECT_EQ(SignalingAddress::Channel::XMPP, to.channel());
-  EXPECT_EQ("", to.endpoint_id());
-  EXPECT_EQ("user@gmail.com/chromiumsy5C6A652D", to.id());
-}
-
-TEST(SignalingAddressTest, ParseInvalidBotAddress) {
-  // Parse a message with LCS address and invalid from field.
-  const char kTestMessage[] =
-      "<cli:iq from='invalid_address@google.com' "
-      "to='user@gmail.com/chromiumsy5C6A652D' type='result' "
-      "      xmlns:cli='jabber:client'>"
-      "  <jingle action='session-info' "
-      "        sid='2227053353' xmlns='urn:xmpp:jingle:1' "
-      "        from-channel='lcs' "
-      "        from-endpoint-id='user@gmail.com/xBrnereror='>"
+      "        sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
       "  </jingle>"
       "</cli:iq>";
 
@@ -125,18 +81,16 @@
   SignalingAddress from =
       SignalingAddress::Parse(message.get(), SignalingAddress::FROM, &error);
   EXPECT_TRUE(from.empty());
-  EXPECT_FALSE(error.empty());
+  EXPECT_TRUE(error.empty());
 }
 
-TEST(SignalingAddressTest, ParseMissingEndpointId) {
-  // Parse a message with a missing endpoint-id field.
+TEST(SignalingAddressTest, ParseMissingSignalingId) {
+  // Parse a message with a missing signaling ID field.
   const char kTestMessage[] =
-      "<cli:iq from='invalid_address@google.com' "
-      "      to='user@gmail.com/chromiumsy5C6A652D' type='set' "
+      "<cli:iq to='user@gmail.com/chromoting_ftl_receiver' type='set' "
       "      xmlns:cli='jabber:client'>"
       "  <jingle action='session-info' "
-      "        sid='2227053353' xmlns='urn:xmpp:jingle:1' "
-      "        from-channel='lcs'>"
+      "        sid='2227053353' xmlns='urn:xmpp:jingle:1'>"
       "  </jingle>"
       "</cli:iq>";
 
@@ -147,7 +101,7 @@
   SignalingAddress from =
       SignalingAddress::Parse(message.get(), SignalingAddress::FROM, &error);
   EXPECT_TRUE(from.empty());
-  EXPECT_FALSE(error.empty());
+  EXPECT_TRUE(error.empty());
 }
 
 TEST(SignalingAddressTest, SetInMessageToXmpp) {
@@ -155,22 +109,6 @@
   SignalingAddress addr("user@domain.com/chromoting12345");
   addr.SetInMessage(message.get(), SignalingAddress::TO);
   EXPECT_EQ("user@domain.com/chromoting12345", message->Attr(QName("", "to")));
-  jingle_xmpp::XmlElement* jingle =
-      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
-  EXPECT_EQ("", jingle->Attr(QName("", "to-channel")));
-  EXPECT_EQ("", jingle->Attr(QName("", "to-endpoint-id")));
-}
-
-TEST(SignalingAddressTest, SetInMessageToLcs) {
-  std::unique_ptr<jingle_xmpp::XmlElement> message = GetEmptyJingleMessage();
-  SignalingAddress addr(kLcsAddress);
-
-  addr.SetInMessage(message.get(), SignalingAddress::TO);
-  EXPECT_EQ(remoting::kRemotingBotJid, message->Attr(QName("", "to")));
-  jingle_xmpp::XmlElement* jingle =
-      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
-  EXPECT_EQ("lcs", jingle->Attr(QName("", "to-channel")));
-  EXPECT_EQ(kLcsAddress, jingle->Attr(QName("", "to-endpoint-id")));
 }
 
 TEST(SignalingAddressTest, SetInMessageToFtl) {
@@ -179,10 +117,6 @@
 
   addr.SetInMessage(message.get(), SignalingAddress::TO);
   EXPECT_EQ(kFtlAddress, message->Attr(QName("", "to")));
-  jingle_xmpp::XmlElement* jingle =
-      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
-  EXPECT_EQ("", jingle->Attr(QName("", "to-channel")));
-  EXPECT_EQ("", jingle->Attr(QName("", "to-endpoint-id")));
 }
 
 TEST(SignalingAddressTest, SetInMessageFromXmpp) {
@@ -190,22 +124,6 @@
   SignalingAddress addr("user@domain.com/resource");
   addr.SetInMessage(message.get(), SignalingAddress::FROM);
   EXPECT_EQ("user@domain.com/resource", message->Attr(QName("", "from")));
-  jingle_xmpp::XmlElement* jingle =
-      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
-  EXPECT_EQ("", jingle->Attr(QName("", "from-channel")));
-  EXPECT_EQ("", jingle->Attr(QName("", "from-endpoint-id")));
-}
-
-TEST(SignalingAddressTest, SetInMessageFromLcs) {
-  std::unique_ptr<jingle_xmpp::XmlElement> message = GetEmptyJingleMessage();
-  SignalingAddress addr(kLcsAddress);
-
-  addr.SetInMessage(message.get(), SignalingAddress::FROM);
-  EXPECT_EQ(kLcsAddress, message->Attr(QName("", "from")));
-  jingle_xmpp::XmlElement* jingle =
-      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
-  EXPECT_EQ("lcs", jingle->Attr(QName("", "from-channel")));
-  EXPECT_EQ(kLcsAddress, jingle->Attr(QName("", "from-endpoint-id")));
 }
 
 TEST(SignalingAddressTest, SetInMessageFromFtl) {
@@ -213,16 +131,12 @@
   SignalingAddress addr(kFtlAddress);
   addr.SetInMessage(message.get(), SignalingAddress::FROM);
   EXPECT_EQ(kFtlAddress, message->Attr(QName("", "from")));
-  jingle_xmpp::XmlElement* jingle =
-      message->FirstNamed(jingle_xmpp::QName("urn:xmpp:jingle:1", "jingle"));
-  EXPECT_EQ("", jingle->Attr(QName("", "from-channel")));
-  EXPECT_EQ("", jingle->Attr(QName("", "from-endpoint-id")));
 }
 
 TEST(SignalingAddressTest, CreateFtlSignalingAddress) {
   SignalingAddress addr = SignalingAddress::CreateFtlSignalingAddress(
       "user@domain.com", kFtlRegistrationId);
-  EXPECT_EQ(kFtlAddress, addr.jid());
+  EXPECT_EQ(kFtlAddress, addr.id());
 
   std::string username;
   std::string registration_id;
diff --git a/remoting/test/ftl_signaling_playground.cc b/remoting/test/ftl_signaling_playground.cc
index 1c40779..fb9c86c6 100644
--- a/remoting/test/ftl_signaling_playground.cc
+++ b/remoting/test/ftl_signaling_playground.cc
@@ -284,7 +284,7 @@
 
   if (state == SignalStrategy::CONNECTED) {
     HOST_LOG << "Signaling connected. New JID: "
-             << signal_strategy_->GetLocalAddress().jid();
+             << signal_strategy_->GetLocalAddress().id();
     if (on_signaling_connected_callback_) {
       std::move(on_signaling_connected_callback_).Run();
     }
diff --git a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
index 06d3b13..5f3696c 100644
--- a/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
+++ b/services/device/nfc/android/java/src/org/chromium/device/nfc/NfcImpl.java
@@ -18,6 +18,7 @@
 import android.os.Build;
 import android.os.Handler;
 import android.os.Process;
+import android.os.Vibrator;
 import android.util.SparseArray;
 
 import org.chromium.base.Callback;
@@ -119,6 +120,11 @@
      */
     private Runnable mPushTimeoutRunnable;
 
+    /**
+     * Vibrator. @see android.os.Vibrator
+     */
+    private Vibrator mVibrator;
+
     public NfcImpl(int hostId, NfcDelegate delegate) {
         mHostId = hostId;
         mDelegate = delegate;
@@ -148,6 +154,9 @@
                 mNfcAdapter = mNfcManager.getDefaultAdapter();
             }
         }
+
+        mVibrator = (Vibrator) ContextUtils.getApplicationContext().getSystemService(
+                Context.VIBRATOR_SERVICE);
     }
 
     /**
@@ -437,7 +446,8 @@
         mReaderCallbackHandler = new ReaderCallbackHandler(this);
         mNfcAdapter.enableReaderMode(mActivity, mReaderCallbackHandler,
                 NfcAdapter.FLAG_READER_NFC_A | NfcAdapter.FLAG_READER_NFC_B
-                        | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_NFC_V,
+                        | NfcAdapter.FLAG_READER_NFC_F | NfcAdapter.FLAG_READER_NFC_V
+                        | NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                 null);
     }
 
@@ -652,6 +662,7 @@
      * Called by ReaderCallbackHandler when NFC tag is in proximity.
      */
     public void onTagDiscovered(Tag tag) {
+        mVibrator.vibrate(200);
         processPendingOperations(NfcTagHandler.create(tag));
     }
 
diff --git a/services/network/public/cpp/BUILD.gn b/services/network/public/cpp/BUILD.gn
index 1e98081..5003a80 100644
--- a/services/network/public/cpp/BUILD.gn
+++ b/services/network/public/cpp/BUILD.gn
@@ -31,6 +31,8 @@
     "cors/preflight_result.h",
     "cross_thread_shared_url_loader_factory_info.cc",
     "cross_thread_shared_url_loader_factory_info.h",
+    "data_pipe_to_source_stream.cc",
+    "data_pipe_to_source_stream.h",
     "features.cc",
     "features.h",
     "header_util.cc",
@@ -60,6 +62,8 @@
     "simple_url_loader.cc",
     "simple_url_loader.h",
     "simple_url_loader_stream_consumer.h",
+    "source_stream_to_data_pipe.cc",
+    "source_stream_to_data_pipe.h",
     "weak_wrapper_shared_url_loader_factory.cc",
     "weak_wrapper_shared_url_loader_factory.h",
     "wrapper_shared_url_loader_factory.cc",
@@ -198,6 +202,7 @@
     "cors/preflight_cache_unittest.cc",
     "cors/preflight_result_unittest.cc",
     "cross_thread_shared_url_loader_factory_info_unittest.cc",
+    "data_pipe_to_source_stream_unittest.cc",
     "default_credentials_mojom_traits_unittest.cc",
     "digitally_signed_mojom_traits_unittest.cc",
     "header_util_unittest.cc",
@@ -212,6 +217,7 @@
     "network_quality_tracker_unittest.cc",
     "proxy_config_mojom_traits_unittest.cc",
     "simple_url_loader_unittest.cc",
+    "source_stream_to_data_pipe_unittest.cc",
     "url_request_mojom_traits_unittest.cc",
   ]
 
diff --git a/content/browser/loader/data_pipe_to_source_stream.cc b/services/network/public/cpp/data_pipe_to_source_stream.cc
similarity index 96%
rename from content/browser/loader/data_pipe_to_source_stream.cc
rename to services/network/public/cpp/data_pipe_to_source_stream.cc
index 3dab3a2..4812455 100644
--- a/content/browser/loader/data_pipe_to_source_stream.cc
+++ b/services/network/public/cpp/data_pipe_to_source_stream.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/loader/data_pipe_to_source_stream.h"
+#include "services/network/public/cpp/data_pipe_to_source_stream.h"
 
 #include <utility>
 
@@ -10,7 +10,7 @@
 #include "base/bind.h"
 #include "net/base/io_buffer.h"
 
-namespace content {
+namespace network {
 
 DataPipeToSourceStream::DataPipeToSourceStream(
     mojo::ScopedDataPipeConsumerHandle body)
@@ -103,4 +103,4 @@
   body_.reset();
 }
 
-}  // namespace content
+}  // namespace network
diff --git a/content/browser/loader/data_pipe_to_source_stream.h b/services/network/public/cpp/data_pipe_to_source_stream.h
similarity index 72%
rename from content/browser/loader/data_pipe_to_source_stream.h
rename to services/network/public/cpp/data_pipe_to_source_stream.h
index f77287f..4bfd9b2 100644
--- a/content/browser/loader/data_pipe_to_source_stream.h
+++ b/services/network/public/cpp/data_pipe_to_source_stream.h
@@ -2,18 +2,19 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_LOADER_DATA_PIPE_TO_SOURCE_STREAM_H_
-#define CONTENT_BROWSER_LOADER_DATA_PIPE_TO_SOURCE_STREAM_H_
+#ifndef SERVICES_NETWORK_PUBLIC_CPP_DATA_PIPE_TO_SOURCE_STREAM_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_DATA_PIPE_TO_SOURCE_STREAM_H_
 
-#include "content/common/content_export.h"
+#include "base/component_export.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "net/base/completion_once_callback.h"
 #include "net/filter/source_stream.h"
 
-namespace content {
+namespace network {
 
-class CONTENT_EXPORT DataPipeToSourceStream final : public net::SourceStream {
+class COMPONENT_EXPORT(NETWORK_CPP) DataPipeToSourceStream final
+    : public net::SourceStream {
  public:
   explicit DataPipeToSourceStream(mojo::ScopedDataPipeConsumerHandle body);
   ~DataPipeToSourceStream() override;
@@ -39,6 +40,6 @@
   DISALLOW_COPY_AND_ASSIGN(DataPipeToSourceStream);
 };
 
-}  // namespace content
+}  // namespace network
 
-#endif  // CONTENT_BROWSER_LOADER_DATA_PIPE_TO_SOURCE_STREAM_H_
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_DATA_PIPE_TO_SOURCE_STREAM_H_
diff --git a/content/browser/loader/data_pipe_to_source_stream_unittest.cc b/services/network/public/cpp/data_pipe_to_source_stream_unittest.cc
similarity index 97%
rename from content/browser/loader/data_pipe_to_source_stream_unittest.cc
rename to services/network/public/cpp/data_pipe_to_source_stream_unittest.cc
index 54950b5..63f8fff 100644
--- a/content/browser/loader/data_pipe_to_source_stream_unittest.cc
+++ b/services/network/public/cpp/data_pipe_to_source_stream_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/loader/data_pipe_to_source_stream.h"
+#include "services/network/public/cpp/data_pipe_to_source_stream.h"
 
 #include "base/strings/string_piece.h"
 #include "base/test/task_environment.h"
@@ -10,7 +10,7 @@
 #include "net/base/test_completion_callback.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace content {
+namespace network {
 
 namespace {
 
@@ -172,4 +172,4 @@
   EXPECT_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
 }
 
-}  // namespace content
+}  // namespace network
diff --git a/content/browser/loader/source_stream_to_data_pipe.cc b/services/network/public/cpp/source_stream_to_data_pipe.cc
similarity index 96%
rename from content/browser/loader/source_stream_to_data_pipe.cc
rename to services/network/public/cpp/source_stream_to_data_pipe.cc
index d9b8c51..929d02e9 100644
--- a/content/browser/loader/source_stream_to_data_pipe.cc
+++ b/services/network/public/cpp/source_stream_to_data_pipe.cc
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/loader/source_stream_to_data_pipe.h"
+#include "services/network/public/cpp/source_stream_to_data_pipe.h"
 
 #include "base/bind.h"
 #include "net/filter/source_stream.h"
 
-namespace content {
+namespace network {
 
 SourceStreamToDataPipe::SourceStreamToDataPipe(
     std::unique_ptr<net::SourceStream> source,
@@ -99,4 +99,4 @@
   std::move(completion_callback_).Run(result);
 }
 
-}  // namespace content
+}  // namespace network
diff --git a/content/browser/loader/source_stream_to_data_pipe.h b/services/network/public/cpp/source_stream_to_data_pipe.h
similarity index 80%
rename from content/browser/loader/source_stream_to_data_pipe.h
rename to services/network/public/cpp/source_stream_to_data_pipe.h
index 899b802..327b751 100644
--- a/content/browser/loader/source_stream_to_data_pipe.h
+++ b/services/network/public/cpp/source_stream_to_data_pipe.h
@@ -2,12 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_BROWSER_LOADER_SOURCE_STREAM_TO_DATA_PIPE_H_
-#define CONTENT_BROWSER_LOADER_SOURCE_STREAM_TO_DATA_PIPE_H_
+#ifndef SERVICES_NETWORK_PUBLIC_CPP_SOURCE_STREAM_TO_DATA_PIPE_H_
+#define SERVICES_NETWORK_PUBLIC_CPP_SOURCE_STREAM_TO_DATA_PIPE_H_
 
 #include "base/callback_forward.h"
+#include "base/component_export.h"
 #include "base/memory/weak_ptr.h"
-#include "content/common/content_export.h"
 #include "mojo/public/cpp/system/data_pipe.h"
 #include "mojo/public/cpp/system/simple_watcher.h"
 #include "services/network/public/cpp/net_adapters.h"
@@ -16,11 +16,11 @@
 class SourceStream;
 }
 
-namespace content {
+namespace network {
 
 // A convenient adapter class to read out data from net::SourceStream
 // and write them into a data pipe.
-class CONTENT_EXPORT SourceStreamToDataPipe {
+class COMPONENT_EXPORT(NETWORK_CPP) SourceStreamToDataPipe {
  public:
   // Reads out the data from |source| and write into |dest|.
   SourceStreamToDataPipe(std::unique_ptr<net::SourceStream> source,
@@ -50,6 +50,6 @@
   base::WeakPtrFactory<SourceStreamToDataPipe> weak_factory_{this};
 };
 
-}  // namespace content
+}  // namespace network
 
-#endif  // CONTENT_BROWSER_LOADER_SOURCE_STREAM_TO_DATA_PIPE_H_
+#endif  // SERVICES_NETWORK_PUBLIC_CPP_SOURCE_STREAM_TO_DATA_PIPE_H_
diff --git a/content/browser/loader/source_stream_to_data_pipe_unittest.cc b/services/network/public/cpp/source_stream_to_data_pipe_unittest.cc
similarity index 97%
rename from content/browser/loader/source_stream_to_data_pipe_unittest.cc
rename to services/network/public/cpp/source_stream_to_data_pipe_unittest.cc
index cac914a..5c0e737b 100644
--- a/content/browser/loader/source_stream_to_data_pipe_unittest.cc
+++ b/services/network/public/cpp/source_stream_to_data_pipe_unittest.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/browser/loader/source_stream_to_data_pipe.h"
+#include "services/network/public/cpp/source_stream_to_data_pipe.h"
 
 #include "base/bind.h"
 #include "base/optional.h"
@@ -10,7 +10,7 @@
 #include "net/filter/mock_source_stream.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
-namespace content {
+namespace network {
 
 namespace {
 
@@ -196,4 +196,4 @@
   EXPECT_EQ(*CallbackResult(), net::ERR_ABORTED);
 }
 
-}  // namespace content
+}  // namespace network
diff --git a/styleguide/gender_neutral_code.md b/styleguide/gender_neutral_code.md
index 40b093c..83711cf 100644
--- a/styleguide/gender_neutral_code.md
+++ b/styleguide/gender_neutral_code.md
@@ -1,95 +1 @@
-# Gender-neutral Chromium code
-
-## Why this is important
-
-Some points in our code, documentation and comments contain needless assumptions
-about the gender of a future reader, user, etc. __Example: "When the user logs
-into his profile."__
-
-Our [Code of Conduct](https://www.chromium.org/conduct) under "Be respectful and
-constructive" says:
-
-> Each of us has the right to __enjoy our experience__ and participate without
-> fear of harassment, __discrimination__, or __condescension__, whether blatant
-> or subtle.
-
-Emphasis is added: unnecessarily gendered code is discriminatory and
-condescending, and reading biased code isn't enjoyable.
-
-## Suggestions on how to keep code gender-neutral
-
-These are only suggestions. You make the call. 
-
-Things to avoid:
-
-* Gendered pronouns: he / she / him / her / his / hers, etc.
-* Instances of the phrases "he or she", "his/hers", "(s)he", etc. All of these
-  still exclude those who don't identify with either gender, and implicitly
-  (slightly) favor one gender via listing it first.
-* "Guys" as a gender-neutral term, which has male associations. Usually in
-  comments it implies anthropomorphism of inanimate objects and should be
-  replaced with a more precise technical term. If it does refer to people,
-  consider using "everyone", "folks", "people", "peeps", "y'all", etc.
-* Other gendered words: "brother", "mother", "man", etc.
-
-Cases that are likely fine to leave alone include:
-
-* References to a specific person ("Rachel is on leave; update this when she is
-  back.").
-* A name ("Guy" and "He" are both valid names).
-* A language code ("he" is the ISO 639-1 language code for Hebrew).
-* He as an abbreviation for "helium".
-* The Spanish word "he".
-* References to a specific fictional person ([Alice, Bob, ...](http://en.wikipedia.org/wiki/Alice_and_Bob)).
-  * For new code/comments, consider using just 'A', 'B' as names.
-* Quotations and content of things like public-domain books.
-* Partner agreements and legal documents we can no longer edit.
-* Occurrences in randomly generated strings or base-64 encodings.
-* Content in a language other than English unless you are fluent in that
-  language.
-
-How to change the remaining awkward intrusions of gender:
-
-* Try rewording things to not involve a pronoun at all. In many cases this makes
-  the documentation clearer. Example: "I tell him when I am all done." → "I tell
-  the owner when I am all done." This saves the reader a tiny bit of mental
-  pointer-dereferencing.
-* Try using [singular they](https://en.wikipedia.org/wiki/Singular_they).
-* Try making hypothetical people plural. "When the user is done he'll
-  probably..."
-→ "When users complete this step, they probably...".
-* When referring to a non-person, "it" or "one" may be good alternatives ([wikipedia link](http://wikipedia.org/wiki/Gender-specific_and_gender-neutral_pronouns#It_and_one_as_gender-neutral_pronouns)).
-
-## Example changelists
-
-For a long list of changes, see [this bug](https://crbug.com/542537). Some
-examples:
-
-* [Make some code gender neutral](https://crrev.com/e3496e19094cf7e711508fe69b197aa13725c790)
-* [Updates comments in the src files to remove gender specific terms](https://crrev.com/5b9d52e8a6ec9c11a675bae21c7d1b0448689fb6)
-* [Gender-neutralize a few more comments / strings](https://crrev.com/993006d919c7b0d3e2309041c1e45b8d7fc7ff0e)
-* [Make some android code gender neutral](https://crrev.com/93d83ac96c3d1c27be9ea7e842b25b3dded2550b)
-
-## Tools
-
-* [Here](https://cs.chromium.org/search/?q=%28%5E%7C%5Cs%7C%5C%28%7C%5C%5B%29%28%5BHh%5De%7C%5BSs%5Dhe%7C%5BHh%5Dis%7C%5BHh%5Ders?%7C%5BHh%5Dim%7C%5BHh%5Der%7C%5BGg%5Duys?%29%5Cb&sq=package:chromium&type=cs)
-  is a code search link prepopulated with a search that finds a lot of gendered
-  terms.
-* To search for files containing gendered terms, use this command (or a variant
-  of it):
-```
-git grep -wiEIl \ '(he)|(she)|(his)|(hers)|(him)|(her)|(guy)|(guys)'
-```
-* To search in a file open in vim for gendered terms, use this command:
-```
-/\<he\>\|\<she\>\|\<his\>\|\<hers\>\|\<him\>\|\<her\>\|\<guy\>\|\<guys\>|\<man\>\c
-```
-* There are presubmit checks available for this that are run for the infra and
-  v8 repos. They are not run for other repos as there are too many false
-  positives.
-
-## Thanks
-
-This document is based on an internal Google document by Rachel Grey and others,
-which can be found [here](https://goto.google.com/gender-neutral-code) (sorry,
-Googler only).
+See the [inclusive code guidelines](inclusive_code.md#gender_neutral).
diff --git a/styleguide/inclusive_code.md b/styleguide/inclusive_code.md
new file mode 100644
index 0000000..22a0343
--- /dev/null
+++ b/styleguide/inclusive_code.md
@@ -0,0 +1,116 @@
+# Inclusive Chromium code
+
+## Why this is important
+
+Our [Code of Conduct](https://www.chromium.org/conduct) under "Be respectful and
+constructive" says:
+
+> Each of us has the right to __enjoy our experience__ and participate without
+> fear of harassment, __discrimination__, or __condescension__, whether blatant
+> or subtle.
+
+Emphasis is added: unnecessarily exclusive code is discriminatory and
+condescending, and reading biased code isn't enjoyable.
+
+## Gender-Neutral
+
+Some points in our code, documentation and comments contain needless assumptions
+about the gender of a future reader, user, etc. __Example: "When the user logs
+into his profile."__
+
+### Suggestions on how to keep code gender-neutral
+
+These are only suggestions. You make the call.
+
+Things to avoid:
+
+* Gendered pronouns: he / she / him / her / his / hers, etc.
+* Instances of the phrases "he or she", "his/hers", "(s)he", etc. All of these
+  still exclude those who don't identify with either gender, and implicitly
+  (slightly) favor one gender via listing it first.
+* "Guys" as a gender-neutral term, which has male associations. Usually in
+  comments it implies anthropomorphism of inanimate objects and should be
+  replaced with a more precise technical term. If it does refer to people,
+  consider using "everyone", "folks", "people", "peeps", "y'all", etc.
+* Other gendered words: "brother", "mother", "man", etc.
+
+Cases that are likely fine to leave alone include:
+
+* References to a specific person ("Rachel is on leave; update this when she is
+  back.").
+* A name ("Guy" and "He" are both valid names).
+* A language code ("he" is the ISO 639-1 language code for Hebrew).
+* He as an abbreviation for "helium".
+* The Spanish word "he".
+* References to a specific fictional person ([Alice, Bob, ...](http://en.wikipedia.org/wiki/Alice_and_Bob)).
+  * For new code/comments, consider using just 'A', 'B' as names.
+* Quotations and content of things like public-domain books.
+* Partner agreements and legal documents we can no longer edit.
+* Occurrences in randomly generated strings or base-64 encodings.
+* Content in a language other than English unless you are fluent in that
+  language.
+
+How to change the remaining awkward intrusions of gender:
+
+* Try rewording things to not involve a pronoun at all. In many cases this makes
+  the documentation clearer. Example: "I tell him when I am all done." → "I tell
+  the owner when I am all done." This saves the reader a tiny bit of mental
+  pointer-dereferencing.
+* Try using [singular they](https://en.wikipedia.org/wiki/Singular_they).
+* Try making hypothetical people plural. "When the user is done he'll
+  probably..."
+→ "When users complete this step, they probably...".
+* When referring to a non-person, "it" or "one" may be good alternatives ([wikipedia link](http://wikipedia.org/wiki/Gender-specific_and_gender-neutral_pronouns#It_and_one_as_gender-neutral_pronouns)).
+
+### Example changelists
+
+For a long list of changes, see [this bug](https://crbug.com/542537). Some
+examples:
+
+* [Make some code gender neutral](https://crrev.com/e3496e19094cf7e711508fe69b197aa13725c790)
+* [Updates comments in the src files to remove gender specific terms](https://crrev.com/5b9d52e8a6ec9c11a675bae21c7d1b0448689fb6)
+* [Gender-neutralize a few more comments / strings](https://crrev.com/993006d919c7b0d3e2309041c1e45b8d7fc7ff0e)
+* [Make some android code gender neutral](https://crrev.com/93d83ac96c3d1c27be9ea7e842b25b3dded2550b)
+
+### Tools
+
+* [Here](https://cs.chromium.org/search/?q=%28%5E%7C%5Cs%7C%5C%28%7C%5C%5B%29%28%5BHh%5De%7C%5BSs%5Dhe%7C%5BHh%5Dis%7C%5BHh%5Ders?%7C%5BHh%5Dim%7C%5BHh%5Der%7C%5BGg%5Duys?%29%5Cb&sq=package:chromium&type=cs)
+  is a code search link prepopulated with a search that finds a lot of gendered
+  terms.
+* To search for files containing gendered terms, use this command (or a variant
+  of it):
+```
+git grep -wiEIl \ '(he)|(she)|(his)|(hers)|(him)|(her)|(guy)|(guys)'
+```
+* To search in a file open in vim for gendered terms, use this command:
+```
+/\<he\>\|\<she\>\|\<his\>\|\<hers\>\|\<him\>\|\<her\>\|\<guy\>\|\<guys\>|\<man\>\c
+```
+* There are presubmit checks available for this that are run for the infra and
+  v8 repos. They are not run for other repos as there are too many false
+  positives.
+
+## Racially neutral
+
+Terms such as "blacklist" and "whitelist" reinforce the notion that black==bad
+and white==good. [That Word *Black*, by Langston
+Hughes](https://mcwriting11.blogspot.com/2014/06/that-word-black-by-langston-hughes.html)
+illustrates this problem in a lighthearted, if somewhat pointed way.
+
+These terms can usually be replaced by "blocklist" and "allowlist" without
+changing their meanings, but particular instances may need other replacements.
+
+### Example changelists
+
+For a long list of changes, see [this bug](https://crbug.com/842296). Some
+examples:
+
+* ["Blacklist"->"Blocklist" in interventions-internals UI.](https://crrev.com/1055905)
+* [Remove "whitelist" and "blacklist" from extension docs.](https://crrev.com/1056027)
+* [Declarative Net Request: Replace usages of 'blacklist' and 'whitelist'.](https://crrev.com/1094141)
+
+## Thanks
+
+This document is based on an internal Google document by Rachel Grey and others,
+which can be found [here](https://goto.google.com/gender-neutral-code) (sorry,
+Googlers only).
diff --git a/styleguide/styleguide.md b/styleguide/styleguide.md
index 0d59725..5120b7c 100644
--- a/styleguide/styleguide.md
+++ b/styleguide/styleguide.md
@@ -16,7 +16,7 @@
   * [IDL](https://sites.google.com/a/chromium.org/dev/blink/webidl#TOC-Style)
   * [Jinja style guide](https://sites.google.com/a/chromium.org/dev/developers/jinja#TOC-Style) for [Jinja](https://sites.google.com/a/chromium.org/dev/developers/jinja) templates.
 
-Regardless of the language used, please keep code [gender neutral](gender_neutral_code.md).
+Regardless of the language used, please keep code [inclusive for all contributors](inclusive_code.md).
 
 ## Web languages (JavaScript, HTML, CSS)
 
diff --git a/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java b/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java
index c24864e..6ab21577 100644
--- a/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java
+++ b/testing/android/native_test/java/src/org/chromium/native_test/NativeTest.java
@@ -7,10 +7,12 @@
 import android.app.Activity;
 import android.content.Context;
 import android.content.Intent;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.Process;
+import android.system.Os;
 
 import org.chromium.base.Log;
 import org.chromium.base.annotations.JNINamespace;
@@ -36,6 +38,8 @@
             "org.chromium.native_test.NativeTest.Shard";
     public static final String EXTRA_STDOUT_FILE =
             "org.chromium.native_test.NativeTest.StdoutFile";
+    public static final String EXTRA_COVERAGE_DEVICE_FILE =
+            "org.chromium.native_test.NativeTest.CoverageDeviceFile";
 
     private static final String TAG = "cr_NativeTest";
 
@@ -65,7 +69,14 @@
     }
 
     public void preCreate(Activity activity) {
-        // Empty, but subclasses override.
+        String coverageDeviceFile = activity.getIntent().getStringExtra(EXTRA_COVERAGE_DEVICE_FILE);
+        if (coverageDeviceFile != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+            try {
+                Os.setenv("LLVM_PROFILE_FILE", coverageDeviceFile, true);
+            } catch (Exception e) {
+                Log.w(TAG, "failed to set LLVM_PROFILE_FILE", e);
+            }
+        }
     }
 
     public void postCreate(Activity activity) {
diff --git a/testing/buildbot/chromium.fyi.json b/testing/buildbot/chromium.fyi.json
index 6375cd4..1bd219ac 100644
--- a/testing/buildbot/chromium.fyi.json
+++ b/testing/buildbot/chromium.fyi.json
@@ -18961,6 +18961,18 @@
       },
       {
         "isolate_coverage_data": true,
+        "isolate_name": "json_web_key_fuzzer",
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
+        },
+        "name": "json_web_key_fuzzer",
+        "swarming": {
+          "can_use_on_swarming_builders": false
+        }
+      },
+      {
+        "isolate_coverage_data": true,
         "isolate_name": "language_detection_fuzzer",
         "merge": {
           "args": [],
diff --git a/testing/buildbot/gn_isolate_map.pyl b/testing/buildbot/gn_isolate_map.pyl
index 6bd68a0..dacfba7 100644
--- a/testing/buildbot/gn_isolate_map.pyl
+++ b/testing/buildbot/gn_isolate_map.pyl
@@ -1461,6 +1461,10 @@
     "label": "//jingle:jingle_unittests",
     "type": "console_test_launcher",
   },
+  "json_web_key_fuzzer": {
+    "label": "//media:json_web_key_fuzzer",
+    "type": "fuzzer",
+  },
   "junit_unit_tests": {
     "label": "//testing/android/junit:junit_unit_tests",
     "type": "junit_test",
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index f7c29331..96b5c45 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -73,6 +73,34 @@
       },
     },
 
+    'android_instrumentation_tests': {
+      'chrome_public_test_apk': {
+        'swarming': {
+          'shards': 20,
+        },
+      },
+      'chrome_public_test_vr_apk': {
+        'args': [
+          '--shared-prefs-file=//chrome/android/shared_preference_files/test/vr_cardboard_skipdon_setupcomplete.json',
+          '--additional-apk=//third_party/gvr-android-sdk/test-apks/vr_services/vr_services_current.apk',
+        ],
+        'swarming': {
+          'shards': 2,
+        },
+      },
+      'content_shell_test_apk': {
+        'swarming': {
+          'shards': 3,
+        },
+      },
+      'mojo_test_apk': {},
+      'webview_instrumentation_test_apk': {
+        'swarming': {
+          'shards': 7,
+        },
+      },
+    },
+
     'android_isolated_scripts': {
       'content_shell_crash_test': {
         'args': [
@@ -1144,6 +1172,11 @@
           'can_use_on_swarming_builders': False,
         },
       },
+      'json_web_key_fuzzer': {
+        'swarming': {
+          'can_use_on_swarming_builders': False,
+        },
+      },
       'language_detection_fuzzer': {
         'swarming': {
           'can_use_on_swarming_builders': False,
diff --git a/testing/buildbot/tryserver.chromium.android.json b/testing/buildbot/tryserver.chromium.android.json
index 21ac4b2..1a12e43 100644
--- a/testing/buildbot/tryserver.chromium.android.json
+++ b/testing/buildbot/tryserver.chromium.android.json
@@ -2,10 +2,6 @@
   "AAAAA1 AUTOGENERATED FILE DO NOT EDIT": {},
   "AAAAA2 See generate_buildbot_json.py to make changes": {},
   "android-marshmallow-arm64-coverage-rel": {
-    "additional_compile_targets": [
-      "monochrome_static_initializers",
-      "weblayer_shell"
-    ],
     "gtest_tests": [
       {
         "args": [
@@ -18,909 +14,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "android_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "android_browsertests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "android_webview_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "android_webview_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "angle_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "angle_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "base_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "base_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "base_util_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "base_util_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "blink_common_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "blink_common_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "blink_heap_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "blink_heap_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "blink_platform_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "blink_platform_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "webkit_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "name": "webkit_unit_tests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 2
-        },
-        "test": "blink_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "boringssl_crypto_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "boringssl_crypto_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "boringssl_ssl_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "boringssl_ssl_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "breakpad_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "breakpad_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "cacheinvalidation_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "cacheinvalidation_unittests"
-      },
-      {
-        "args": [
-          "--gtest_filter=-*UsingRealWebcam*",
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "capture_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "capture_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "cast_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "cast_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "cc_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "cc_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_modern_public_bundle_fake_modules_smoke_test"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "chrome_modern_public_bundle_fake_modules_smoke_test"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_modern_public_bundle_smoke_test"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "chrome_modern_public_bundle_smoke_test"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_modern_public_smoke_test"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "chrome_modern_public_smoke_test"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "chrome_public_smoke_test"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "chrome_public_smoke_test"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "chrome_public_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -1015,188 +108,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "color_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "color_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "components_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "components_browsertests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "components_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 4
-        },
-        "test": "components_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "content_browsertests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 9
-        },
-        "test": "content_browsertests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "content_shell_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -1243,952 +154,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "content_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 3
-        },
-        "test": "content_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "crypto_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "crypto_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "device_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "device_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "display_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "display_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "events_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "events_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gcm_unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gcm_unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gfx_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gfx_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gin_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gin_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gl_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gl_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gl_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gl_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "google_apis_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "google_apis_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gpu_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gpu_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "gwp_asan_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "gwp_asan_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ipc_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "ipc_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "jingle_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "jingle_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "latency_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "latency_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "libjingle_xmpp_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "libjingle_xmpp_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "media_blink_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "media_blink_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "media_service_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "media_service_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "media_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "media_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "midi_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "midi_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "mojo_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -2234,773 +199,6 @@
             "--bucket",
             "chromium-result-details",
             "--test-name",
-            "mojo_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "mojo_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "net_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 3
-        },
-        "test": "net_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "sandbox_linux_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "sandbox_linux_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "services_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "services_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "shell_dialogs_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "shell_dialogs_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "skia_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "skia_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "sql_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "sql_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "storage_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "storage_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ui_android_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "ui_android_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ui_base_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "ui_base_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "ui_touch_selection_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "ui_touch_selection_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "unit_tests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ],
-          "shards": 10
-        },
-        "test": "unit_tests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "url_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "url_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "viz_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "viz_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "vr_android_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "vr_android_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "vr_common_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "vr_common_unittests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "vr_pixeltests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "vr_pixeltests"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
             "webview_instrumentation_test_apk"
           ],
           "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
@@ -3035,148 +233,6 @@
           "shards": 7
         },
         "test": "webview_instrumentation_test_apk"
-      },
-      {
-        "args": [
-          "--gs-results-bucket=chromium-result-details",
-          "--recover-devices"
-        ],
-        "isolate_coverage_data": true,
-        "merge": {
-          "args": [
-            "--bucket",
-            "chromium-result-details",
-            "--test-name",
-            "wtf_unittests"
-          ],
-          "script": "//build/android/pylib/results/presentation/test_results_presentation.py"
-        },
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "cipd_packages": [
-            {
-              "cipd_package": "infra/tools/luci/logdog/butler/${platform}",
-              "location": "bin",
-              "revision": "git_revision:ff387eadf445b24c935f1cf7d6ddd279f8a6b04c"
-            }
-          ],
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ],
-          "output_links": [
-            {
-              "link": [
-                "https://luci-logdog.appspot.com/v/?s",
-                "=android%2Fswarming%2Flogcats%2F",
-                "${TASK_ID}%2F%2B%2Funified_logcats"
-              ],
-              "name": "shard #${SHARD_INDEX} logcats"
-            }
-          ]
-        },
-        "test": "wtf_unittests"
-      }
-    ],
-    "isolated_scripts": [
-      {
-        "args": [
-          "--gtest-benchmark-name=components_perftests"
-        ],
-        "isolate_coverage_data": true,
-        "isolate_name": "components_perftests",
-        "merge": {
-          "args": [
-            "--smoke-test-mode"
-          ],
-          "script": "//tools/perf/process_perf_results.py"
-        },
-        "name": "components_perftests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "--platform=android"
-        ],
-        "isolate_coverage_data": true,
-        "isolate_name": "content_shell_crash_test",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "content_shell_crash_test",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ]
-        }
-      },
-      {
-        "isolate_coverage_data": true,
-        "isolate_name": "monochrome_apk_checker",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "monochrome_apk_checker",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android"
-            }
-          ]
-        }
-      },
-      {
-        "args": [
-          "--browser=android-chromium",
-          "--device=android"
-        ],
-        "isolate_coverage_data": true,
-        "isolate_name": "telemetry_perf_unittests",
-        "merge": {
-          "args": [],
-          "script": "//testing/merge_scripts/standard_isolated_script_merge.py"
-        },
-        "name": "telemetry_perf_unittests",
-        "swarming": {
-          "can_use_on_swarming_builders": true,
-          "dimension_sets": [
-            {
-              "device_os": "MMB29Q",
-              "device_os_type": "userdebug",
-              "device_type": "bullhead",
-              "os": "Android",
-              "temp_band": "<30"
-            }
-          ],
-          "idempotent": false,
-          "shards": 15
-        }
       }
     ],
     "junit_tests": [
diff --git a/testing/buildbot/waterfalls.pyl b/testing/buildbot/waterfalls.pyl
index 9f359d0..57a263da 100644
--- a/testing/buildbot/waterfalls.pyl
+++ b/testing/buildbot/waterfalls.pyl
@@ -4402,14 +4402,9 @@
           'marshmallow',
           'bullhead',
         ],
-        'additional_compile_targets': [
-          'monochrome_static_initializers',
-          'weblayer_shell',
-        ],
         'test_suites': {
-          'gtest_tests': 'android_lollipop_marshmallow_gtests',
+          'gtest_tests': 'android_instrumentation_tests',
           'junit_tests': 'chromium_junit_tests',
-          'isolated_scripts': 'marshmallow_pie_isolated_scripts',
         },
         'os_type': 'android',
       },
diff --git a/third_party/blink/common/features.cc b/third_party/blink/common/features.cc
index aff73cf..c1abf6d 100644
--- a/third_party/blink/common/features.cc
+++ b/third_party/blink/common/features.cc
@@ -18,9 +18,9 @@
     "BlockingDownloadsInAdFrameWithoutUserActivation",
     base::FEATURE_ENABLED_BY_DEFAULT};
 
-// Enable defer commits a bit to avoid flash.
-const base::Feature kAvoidFlashBetweenNavigation{
-    "AvoidFlashBetweenNavigation", base::FEATURE_ENABLED_BY_DEFAULT};
+// Enable defer commits to avoid flash of unstyled content.
+const base::Feature kPaintHolding{"PaintHolding",
+                                  base::FEATURE_ENABLED_BY_DEFAULT};
 
 // Enable eagerly setting up a CacheStorage interface pointer and
 // passing it to service workers on startup as an optimization.
@@ -400,7 +400,7 @@
     base::FEATURE_ENABLED_BY_DEFAULT};
 
 const base::Feature kHtmlImportsRequestInitiatorLock{
-    "HtmlImportsRequestInitiatorLock", base::FEATURE_DISABLED_BY_DEFAULT};
+    "HtmlImportsRequestInitiatorLock", base::FEATURE_ENABLED_BY_DEFAULT};
 
 }  // namespace features
 }  // namespace blink
diff --git a/third_party/blink/public/BUILD.gn b/third_party/blink/public/BUILD.gn
index b04dc16..91c59c3 100644
--- a/third_party/blink/public/BUILD.gn
+++ b/third_party/blink/public/BUILD.gn
@@ -34,6 +34,7 @@
     ":test_headers",
     "//third_party/blink/renderer/modules/exported:test_support",
     "//third_party/blink/renderer/modules/mediastream:test_support",
+    "//third_party/blink/renderer/modules/peerconnection:test_support",
     "//third_party/blink/renderer/platform:test_support",
   ]
 }
@@ -103,12 +104,22 @@
     "web/modules/mediastream/mock_media_stream_registry.h",
     "web/modules/mediastream/mock_media_stream_video_sink.h",
     "web/modules/mediastream/mock_media_stream_video_source.h",
+
+    # TODO(crbug.com/787254): Remove the group of mock_.h files
+    # below when content/renderer/media/webrtc Onion souping is
+    # done.
+    "web/modules/peerconnection/mock_data_channel_impl.h",
+    "web/modules/peerconnection/mock_peer_connection_dependency_factory.h",
+    "web/modules/peerconnection/mock_peer_connection_impl.h",
   ]
   deps = [
     ":blink_headers",
     "//base:base",
     "//cc:cc",
     "//testing/gmock:gmock",
+    "//third_party/webrtc/api:libjingle_peerconnection_api",
+    "//third_party/webrtc/api:media_stream_interface",
+    "//third_party/webrtc/api:rtc_stats_api",
   ]
 }
 
diff --git a/third_party/blink/public/common/features.h b/third_party/blink/public/common/features.h
index fb1d2d47..e9b578f 100644
--- a/third_party/blink/public/common/features.h
+++ b/third_party/blink/public/common/features.h
@@ -17,7 +17,7 @@
 
 BLINK_COMMON_EXPORT extern const base::Feature
     kBlockingDownloadsInAdFrameWithoutUserActivation;
-BLINK_COMMON_EXPORT extern const base::Feature kAvoidFlashBetweenNavigation;
+BLINK_COMMON_EXPORT extern const base::Feature kPaintHolding;
 BLINK_COMMON_EXPORT extern const base::Feature
     kEagerCacheStorageSetupForServiceWorkers;
 BLINK_COMMON_EXPORT extern const base::Feature kScriptStreaming;
diff --git a/third_party/blink/public/web/DEPS b/third_party/blink/public/web/DEPS
index e2f15930..5ab2645 100644
--- a/third_party/blink/public/web/DEPS
+++ b/third_party/blink/public/web/DEPS
@@ -27,6 +27,7 @@
     "+services/metrics/public/cpp/ukm_source_id.h",
     "+services/network/public/mojom/cors.mojom-shared.h",
     "+services/network/public/mojom/cors_origin_pattern.mojom-shared.h",
+    "+services/network/public/mojom/fetch_api.mojom-shared.h",
     "+services/network/public/mojom/ip_address_space.mojom-shared.h",
     "+services/network/public/mojom/referrer_policy.mojom-shared.h",
     "+services/service_manager/public",
diff --git a/third_party/blink/public/web/modules/peerconnection/DEPS b/third_party/blink/public/web/modules/peerconnection/DEPS
index 8e186078..e2f8772b 100644
--- a/third_party/blink/public/web/modules/peerconnection/DEPS
+++ b/third_party/blink/public/web/modules/peerconnection/DEPS
@@ -8,4 +8,5 @@
     "+base/single_thread_task_runner.h",
     "+base/synchronization/waitable_event.h",
     "+base/threading/thread.h",
+    "+testing/gmock/include/gmock/gmock.h",
 ]
diff --git a/content/renderer/media/webrtc/mock_data_channel_impl.h b/third_party/blink/public/web/modules/peerconnection/mock_data_channel_impl.h
similarity index 77%
rename from content/renderer/media/webrtc/mock_data_channel_impl.h
rename to third_party/blink/public/web/modules/peerconnection/mock_data_channel_impl.h
index 6fbf726b..9dd6311 100644
--- a/content/renderer/media/webrtc/mock_data_channel_impl.h
+++ b/third_party/blink/public/web/modules/peerconnection/mock_data_channel_impl.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 CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_DATA_CHANNEL_IMPL_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_DATA_CHANNEL_IMPL_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_DATA_CHANNEL_IMPL_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_DATA_CHANNEL_IMPL_H_
 
 #include <stdint.h>
 
@@ -12,8 +12,10 @@
 #include "base/macros.h"
 #include "third_party/webrtc/api/peer_connection_interface.h"
 
-namespace content {
+namespace blink {
 
+// TODO(crbug.com/787254): Move this class out of the Blink API
+// when all its clients get Onion souped.
 class MockDataChannel : public webrtc::DataChannelInterface {
  public:
   MockDataChannel(const std::string& label,
@@ -52,6 +54,6 @@
   DISALLOW_COPY_AND_ASSIGN(MockDataChannel);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_DATA_CHANNEL_IMPL_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_DATA_CHANNEL_IMPL_H_
diff --git a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h b/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h
similarity index 93%
rename from content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
rename to third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h
index dfc03efd..55454164b 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h
+++ b/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h
@@ -2,20 +2,22 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_PEER_CONNECTION_DEPENDENCY_FACTORY_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_PEER_CONNECTION_DEPENDENCY_FACTORY_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_PEER_CONNECTION_DEPENDENCY_FACTORY_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_PEER_CONNECTION_DEPENDENCY_FACTORY_H_
 
 #include <set>
 #include <string>
 #include <vector>
 
-#include "base/compiler_specific.h"
 #include "base/macros.h"
-#include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/web/modules/peerconnection/peer_connection_dependency_factory.h"
 #include "third_party/webrtc/api/media_stream_interface.h"
 
-namespace content {
+namespace base {
+class SingleThreadTaskRunner;
+}
+
+namespace blink {
 
 typedef std::set<webrtc::ObserverInterface*> ObserverSet;
 
@@ -170,6 +172,6 @@
   DISALLOW_COPY_AND_ASSIGN(MockPeerConnectionDependencyFactory);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_PEER_CONNECTION_DEPENDENCY_FACTORY_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_PEER_CONNECTION_DEPENDENCY_FACTORY_H_
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.h b/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h
similarity index 95%
rename from content/renderer/media/webrtc/mock_peer_connection_impl.h
rename to third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h
index 7e35cc7..72a64fd 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.h
+++ b/third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h
@@ -2,14 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_PEER_CONNECTION_IMPL_H_
-#define CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_PEER_CONNECTION_IMPL_H_
+#ifndef THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_PEER_CONNECTION_IMPL_H_
+#define THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_PEER_CONNECTION_IMPL_H_
 
 #include <memory>
 #include <string>
 
-#include "base/compiler_specific.h"
-#include "base/logging.h"
 #include "base/macros.h"
 #include "base/optional.h"
 #include "testing/gmock/include/gmock/gmock.h"
@@ -19,7 +17,7 @@
 #include "third_party/webrtc/api/stats/rtc_stats_report.h"
 #include "third_party/webrtc/api/test/dummy_peer_connection.h"
 
-namespace content {
+namespace blink {
 
 class MockPeerConnectionDependencyFactory;
 class MockStreamCollection;
@@ -212,9 +210,9 @@
   }
   MOCK_CONST_METHOD0(GetSctpTransport,
                      rtc::scoped_refptr<webrtc::SctpTransportInterface>());
-  rtc::scoped_refptr<webrtc::DataChannelInterface>
-      CreateDataChannel(const std::string& label,
-                        const webrtc::DataChannelInit* config) override;
+  rtc::scoped_refptr<webrtc::DataChannelInterface> CreateDataChannel(
+      const std::string& label,
+      const webrtc::DataChannelInit* config) override;
 
   bool GetStats(webrtc::StatsObserver* observer,
                 webrtc::MediaStreamTrackInterface* track,
@@ -351,9 +349,7 @@
   webrtc::SessionDescriptionInterface* created_session_description() const {
     return created_sessiondescription_.get();
   }
-  webrtc::PeerConnectionObserver* observer() {
-    return observer_;
-  }
+  webrtc::PeerConnectionObserver* observer() { return observer_; }
   void set_setconfiguration_error_type(webrtc::RTCErrorType error_type) {
     setconfiguration_error_type_ = error_type;
   }
@@ -390,6 +386,6 @@
   DISALLOW_COPY_AND_ASSIGN(MockPeerConnectionImpl);
 };
 
-}  // namespace content
+}  // namespace blink
 
-#endif  // CONTENT_RENDERER_MEDIA_WEBRTC_MOCK_PEER_CONNECTION_IMPL_H_
+#endif  // THIRD_PARTY_BLINK_PUBLIC_WEB_MODULES_PEERCONNECTION_MOCK_PEER_CONNECTION_IMPL_H_
diff --git a/third_party/blink/public/web/web_local_frame.h b/third_party/blink/public/web/web_local_frame.h
index 7cf9159d..026cefa 100644
--- a/third_party/blink/public/web/web_local_frame.h
+++ b/third_party/blink/public/web/web_local_frame.h
@@ -9,7 +9,7 @@
 #include <set>
 
 #include "base/callback.h"
-#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
+#include "services/network/public/mojom/fetch_api.mojom-shared.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/frame/sandbox_flags.h"
 #include "third_party/blink/public/common/messaging/transferable_message.h"
diff --git a/third_party/blink/public/web/web_local_frame_client.h b/third_party/blink/public/web/web_local_frame_client.h
index f1ccc5a..e2db541 100644
--- a/third_party/blink/public/web/web_local_frame_client.h
+++ b/third_party/blink/public/web/web_local_frame_client.h
@@ -34,6 +34,7 @@
 #include <memory>
 
 #include "base/unguessable_token.h"
+#include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h"
 #include "third_party/blink/public/common/feature_policy/feature_policy.h"
 #include "third_party/blink/public/common/frame/blocked_navigation_types.h"
 #include "third_party/blink/public/common/frame/frame_owner_element_type.h"
@@ -348,10 +349,10 @@
   // The client should handle the request as a download.
   // If the request is for a blob: URL, a BlobURLToken should be provided
   // as |blob_url_token| to ensure the correct blob gets downloaded.
-  enum class CrossOriginRedirects { kFollow, kNavigate };
-  virtual void DownloadURL(const WebURLRequest&,
-                           CrossOriginRedirects cross_origin_redirect_behavior,
-                           mojo::ScopedMessagePipeHandle blob_url_token) {}
+  virtual void DownloadURL(
+      const WebURLRequest&,
+      network::mojom::RedirectMode cross_origin_redirect_behavior,
+      mojo::ScopedMessagePipeHandle blob_url_token) {}
 
   // Navigational queries ------------------------------------------------
 
diff --git a/third_party/blink/renderer/bindings/BUILD.gn b/third_party/blink/renderer/bindings/BUILD.gn
index 131f8bc..6576123 100644
--- a/third_party/blink/renderer/bindings/BUILD.gn
+++ b/third_party/blink/renderer/bindings/BUILD.gn
@@ -134,7 +134,6 @@
 
   inputs = [
     web_idl_database,
-    "${bindings_scripts_dir}/bind_gen/example.cc.tmpl",
   ]
   outputs = [
     "${bindings_output_dir}/core/v8/v8_example.cc",
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node.py b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node.py
new file mode 100644
index 0000000..c0559e4
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node.py
@@ -0,0 +1,438 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+"""
+The code generator generates code based on a graph of code fragments.  Each node
+of the graph is represented with CodeNode and its subclasses.  This module
+provides a collection of the classes that represent code nodes independent from
+specific bindings, such as ECMAScript bindings.
+"""
+
+import copy
+import string
+
+from .mako_renderer import MakoRenderer
+
+
+class CodeNode(object):
+    """
+    This is the base class of all code fragment nodes.
+
+    - Graph structure
+    CodeNode can be nested and |outer| points to the nesting CodeNode.  Also
+    CodeNode can make a sequence and |prev| points to the previous CodeNode.
+    See also |SequenceNode|.
+
+    - Template rendering
+    CodeNode has template text and template variable bindings.  Either of
+    |__str__| or |render| returns a text of generated code.  However, these
+    methods have side effects on rendering states, and repeated calls may return
+    different results.
+    """
+
+    class _RenderState(object):
+        """
+        Represents a set of per-render states.  Every call to CodeNode.render
+        resets all the per-render states.
+        """
+
+        def __init__(self):
+            # Symbols used in generated code, but not yet defined.  See also
+            # SymbolNode.
+            self.code_symbols_used = {}
+
+    class _LooseFormatter(string.Formatter):
+        def get_value(self, key, args, kwargs):
+            if key in kwargs:
+                return kwargs[key]
+            else:
+                return '{' + key + '}'
+
+    _template_formatter = _LooseFormatter()
+    _gensym_seq_id = 0
+
+    @classmethod
+    def format_template(cls, format_string, *args, **kwargs):
+        """
+        Formats a string like the built-in |format| allowing unbound keys.
+
+            format_template("${template_var} {format_var}", format_var=42)
+        will produce
+            "${template_var} 42"
+        without raising an exception that |template_var| is unbound.
+        """
+        return cls._template_formatter.format(format_string, *args, **kwargs)
+
+    @classmethod
+    def gensym(cls):
+        """
+        Creates a new template variable that never conflicts with anything.
+
+        The name 'gensym' came from 'gensym' (generated symbol) in Lisp that
+        exists for exactly the same purpose.
+
+        Note that |gensym| is used to produce a new Mako template variable while
+        SymbolNode is used to represent a code symbol (such as a local variable)
+        in generated code.
+
+        Bad example:
+            template_text = "abc ${tmp} xyz"
+            a = CodeNodeA(template_text='123')
+            b = CodeNodeB(template_text=template_text, {'tmp': a})
+        |b| expects "abc 123 xyz" but what if 'tmp' were already bound to
+        something else?
+
+        Good example:
+            sym = CodeNode.gensym()
+            template_text = CodeNode.format_template(
+                "abc ${{{node_a}}} xyz", node_a=sym)
+            a = CodeNodeA(template_text='123')
+            b = CodeNodeB(template_text=template_text, {sym: a})
+        "{{" and "}}" are literal of "{" and "}" themselves, and the innermost
+        "{node_a}" will be replaced with |sym|.  The resulting template text
+        will be "abc ${gensym1} xyz" when |sym| is 'gensym1'.
+        """
+        cls._gensym_seq_id += 1
+        return 'gensym{}'.format(cls._gensym_seq_id)
+
+    def __init__(self,
+                 outer=None,
+                 prev=None,
+                 template_text=None,
+                 template_vars=None,
+                 renderer=None):
+        assert outer is None or isinstance(outer, CodeNode)
+        assert prev is None or isinstance(prev, CodeNode)
+        assert template_text is None or isinstance(template_text, str)
+        assert template_vars is None or isinstance(template_vars, dict)
+        assert renderer is None or isinstance(renderer, MakoRenderer)
+
+        # The outer CodeNode or None iff this is a top-level node
+        self._outer = outer
+        # The previous CodeNode if this is a Sequence or None
+        self._prev = prev
+
+        # Mako's template text, bindings dict, and the renderer object
+        self._template_text = template_text
+        self._template_vars = {}
+        self._renderer = renderer
+
+        self._render_state = CodeNode._RenderState()
+        self._is_rendering = False
+
+        if template_vars:
+            self.add_template_vars(template_vars)
+
+    def __str__(self):
+        """
+        Renders this CodeNode object into a Mako template.  This is supposed to
+        be used in a Mako template as ${code_node}.
+        """
+        return self.render()
+
+    def render(self):
+        """
+        Renders this CodeNode object as a text string and also propagates
+        updates to related CodeNode objects.  As this method has side-effects
+        not only to this object but also other related objects, the resulting
+        text may change on each invocation.
+        """
+        assert self.renderer
+
+        last_render_state = self._render_state
+        self._render_state = CodeNode._RenderState()
+        self._is_rendering = True
+
+        try:
+            text = self._render(
+                renderer=self.renderer, last_render_state=last_render_state)
+        finally:
+            self._is_rendering = False
+
+        return text
+
+    def _render(self, renderer, last_render_state):
+        """
+        Renders this CodeNode object as a text string and also propagates
+        updates to related CodeNode objects.
+
+        Only limited subclasses may override this method.
+        """
+        assert self._template_text
+        return renderer.render(
+            caller=self,
+            template_text=self._template_text,
+            template_vars=self.template_vars)
+
+    @property
+    def outer(self):
+        """Returns the outer CodeNode or None iff this is a top-level node."""
+        return self._outer
+
+    def set_outer(self, outer):
+        assert isinstance(outer, CodeNode)
+        assert self._outer is None
+        self._outer = outer
+
+    @property
+    def prev(self):
+        """Returns the previous CodeNode if this is a Sequence or None."""
+        return self._prev
+
+    def set_prev(self, prev):
+        assert isinstance(prev, CodeNode)
+        assert self._prev is None
+        self._prev = prev
+
+    def reset_prev(self, prev):
+        assert isinstance(prev, CodeNode)
+        self._prev = prev
+
+    @property
+    def upstream(self):
+        """
+        Returns the upstream CodeNode in terms of code flow.
+
+        The upstream CodeNode is defined as:
+        1. the previous CodeNode, or
+        2. the outer CodeNode, or
+        3. None (as this is a top-level node)
+        """
+        return self.prev or self.outer
+
+    @property
+    def template_vars(self):
+        """
+        Returns the template variable bindings available at this point, i.e.
+        bound at this node or outer nodes.
+
+        CAUTION: Do not modify the returned dict.  This method may return the
+        original dict in a CodeNode.
+        """
+        if not self.outer:
+            return self._template_vars
+
+        if not self._template_vars:
+            return self.outer.template_vars
+
+        binds = copy.copy(self.outer.template_vars)
+        for name, value in self._template_vars.iteritems():
+            assert name not in binds, (
+                "Duplicated template variable binding: {}".format(name))
+            binds[name] = value
+        return binds
+
+    def add_template_var(self, name, value):
+        assert name not in self._template_vars
+        if isinstance(value, CodeNode):
+            value.set_outer(self)
+        self._template_vars[name] = value
+
+    def add_template_vars(self, template_vars):
+        assert isinstance(template_vars, dict)
+        for name, value in template_vars.iteritems():
+            self.add_template_var(name, value)
+
+    @property
+    def renderer(self):
+        return self._renderer or self.outer.renderer
+
+    @property
+    def current_render_state(self):
+        assert self._is_rendering
+        return self._render_state
+
+    @property
+    def last_render_state(self):
+        assert not self._is_rendering
+        return self._render_state
+
+    def is_code_symbol_defined(self, symbol_node):
+        """
+        Returns True if |symbol_node| is defined at this point or upstream.
+        """
+        if self.upstream:
+            return self.upstream.is_code_symbol_defined(symbol_node)
+        return False
+
+    def on_code_symbol_used(self, symbol_node):
+        """Receives a report of use of an undefined symbol node."""
+        assert isinstance(symbol_node, SymbolNode)
+        state = self.current_render_state
+        state.code_symbols_used[symbol_node.name] = symbol_node
+
+
+class SimpleNode(CodeNode):
+    """
+    Represents a simple node that never contains a branch nor sequence.
+
+    Technically, you can make this node contain branches and sequences with
+    using template text and variable bindings, however, it's not supported.
+    """
+
+    def __init__(self, template_text, template_vars=None, renderer=None):
+        CodeNode.__init__(
+            self,
+            template_text=template_text,
+            template_vars=template_vars,
+            renderer=renderer)
+
+
+class SequenceNode(CodeNode):
+    """
+    Represents a sequence of nodes.
+
+    If SymbolNodes are used inside this node, this node will attempt to insert
+    corresponding SymbolDefinitionNodes appropriately.
+    """
+
+    def __init__(self, renderer=None):
+        element_nodes_symbol = CodeNode.gensym()
+        element_nodes = []
+        template_text = CodeNode.format_template(
+            """\
+% for node in {element_nodes}:
+${node}
+% endfor\
+""",
+            element_nodes=element_nodes_symbol)
+        template_vars = {element_nodes_symbol: element_nodes}
+
+        CodeNode.__init__(
+            self,
+            template_text=template_text,
+            template_vars=template_vars,
+            renderer=renderer)
+
+        self._element_nodes = element_nodes
+
+    def __getitem__(self, index):
+        return self._element_nodes[index]
+
+    def __iter__(self):
+        return iter(self._element_nodes)
+
+    def __len__(self):
+        return len(self._element_nodes)
+
+    def _render(self, renderer, last_render_state):
+        # Sort nodes in order to generate reproducible code.
+        symbol_nodes = sorted(
+            last_render_state.code_symbols_used.itervalues(),
+            key=lambda node: node.name)
+        for symbol_node in symbol_nodes:
+            self._insert_symbol_definition(symbol_node)
+
+        return super(SequenceNode, self)._render(
+            renderer=renderer, last_render_state=last_render_state)
+
+    def _insert_symbol_definition(self, symbol_node):
+        self.insert(0, symbol_node.create_definition_node())
+
+    def append(self, node):
+        assert isinstance(node, CodeNode)
+        assert node.outer is None and node.prev is None
+
+        if len(self._element_nodes) == 0:
+            self._element_nodes.append(node)
+        else:
+            node.set_prev(self._element_nodes[-1])
+            self._element_nodes.append(node)
+        node.set_outer(self)
+
+    def extend(self, nodes):
+        for node in nodes:
+            self.append(node)
+
+    def insert(self, index, node):
+        assert isinstance(index, (int, long))
+        assert isinstance(node, CodeNode)
+        assert node.outer is None and node.prev is None
+
+        if index < 0:
+            index += len(self._element_nodes)
+        index = max(0, min(index, len(self._element_nodes)))
+
+        if (len(self._element_nodes) == 0
+                or index == len(self._element_nodes)):
+            return self.append(node)
+
+        next_node = self._element_nodes[index]
+        if next_node.prev:
+            node.set_prev(next_node.prev)
+        next_node.reset_prev(node)
+        node.set_outer(self)
+        self._element_nodes.insert(index, node)
+
+
+class SymbolNode(CodeNode):
+    """
+    Represents a code symbol such as a local variable of generated code.
+
+    Used combined with SequenceNode, SymbolDefinitionNode(s) will be
+    automatically inserted iff this symbol is referenced.
+    """
+
+    def __init__(self, name, definition_node_constructor):
+        """
+        Args:
+            name: The name of this code symbol.
+            definition_node_constructor: A callable that creates and returns a
+                new definition node.  This node will be passed as the argument.
+        """
+        assert isinstance(name, str) and name
+        assert callable(definition_node_constructor)
+
+        CodeNode.__init__(self)
+
+        self._name = name
+        self._definition_node_constructor = definition_node_constructor
+
+    def _render(self, renderer, last_render_state):
+        if not renderer.last_caller.is_code_symbol_defined(self):
+            for caller in renderer.callers:
+                assert isinstance(caller, CodeNode)
+                caller.on_code_symbol_used(self)
+
+        return self.name
+
+    @property
+    def name(self):
+        return self._name
+
+    def create_definition_node(self):
+        """Creates a new definition node."""
+        node = self._definition_node_constructor(self)
+        assert isinstance(node, SymbolDefinitionNode)
+        return node
+
+
+class SymbolDefinitionNode(CodeNode):
+    """
+    Represents a definition of a code symbol.
+
+    It's allowed to define the same code symbol multiple times, and most
+    upstream definition(s) are effective.
+    """
+
+    def __init__(self, symbol_node, template_text, template_vars=None):
+        assert isinstance(symbol_node, SymbolNode)
+
+        CodeNode.__init__(
+            self, template_text=template_text, template_vars=template_vars)
+
+        self._symbol_node = symbol_node
+
+    def _render(self, renderer, last_render_state):
+        if (self.upstream
+                and self.upstream.is_code_symbol_defined(self._symbol_node)):
+            return ''
+
+        return super(SymbolDefinitionNode, self)._render(
+            renderer=renderer, last_render_state=last_render_state)
+
+    def is_code_symbol_defined(self, symbol_node):
+        if symbol_node is self._symbol_node:
+            return True
+        return super(SymbolDefinitionNode,
+                     self).is_code_symbol_defined(symbol_node)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_test.py b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_test.py
new file mode 100644
index 0000000..31baea2
--- /dev/null
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/code_node_test.py
@@ -0,0 +1,96 @@
+# Copyright 2019 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import unittest
+
+from .code_node import SequenceNode
+from .code_node import SimpleNode
+from .code_node import SymbolDefinitionNode
+from .code_node import SymbolNode
+from .mako_renderer import MakoRenderer
+
+
+class CodeNodeTest(unittest.TestCase):
+    def render(self, node):
+        prev = ''
+        current = str(node)
+        while current != prev:
+            prev = current
+            current = str(node)
+        return current
+
+    def assertRenderResult(self, node, expected):
+        def simplify(s):
+            return ' '.join(s.split())
+
+        actual = simplify(self.render(node))
+        expected = simplify(expected)
+
+        self.assertEqual(actual, expected)
+
+    def test_list_operations_of_sequence_node(self):
+        renderer = MakoRenderer()
+        root = SequenceNode(renderer=renderer)
+        root.extend([
+            SimpleNode(template_text="2"),
+            SimpleNode(template_text="4"),
+        ])
+        root.insert(1, SimpleNode(template_text="3"))
+        root.insert(0, SimpleNode(template_text="1"))
+        root.insert(100, SimpleNode(template_text="5"))
+        root.append(SimpleNode(template_text="6"))
+        self.assertRenderResult(root, "1 2 3 4 5 6")
+
+    def test_nested_sequence(self):
+        renderer = MakoRenderer()
+        root = SequenceNode(renderer=renderer)
+        nested = SequenceNode()
+        nested.extend([
+            SimpleNode(template_text="2"),
+            SimpleNode(template_text="3"),
+            SimpleNode(template_text="4"),
+        ])
+        root.extend([
+            SimpleNode(template_text="1"),
+            nested,
+            SimpleNode(template_text="5"),
+        ])
+        self.assertRenderResult(root, "1 2 3 4 5")
+
+    def test_symbol_definition_chains(self):
+        renderer = MakoRenderer()
+        root = SequenceNode(renderer=renderer)
+
+        def make_symbol(name, template_text):
+            def constructor(symbol_node):
+                return SymbolDefinitionNode(
+                    symbol_node, template_text=template_text)
+
+            return SymbolNode(
+                name=name, definition_node_constructor=constructor)
+
+        root.add_template_vars({
+            'var1':
+            make_symbol('var1', "int var1 = ${var2} + ${var3};"),
+            'var2':
+            make_symbol('var2', "int var2 = ${var5};"),
+            'var3':
+            make_symbol('var3', "int var3 = ${var4};"),
+            'var4':
+            make_symbol('var4', "int var4 = 1;"),
+            'var5':
+            make_symbol('var5', "int var5 = 2;"),
+        })
+
+        root.append(SimpleNode(template_text="(void)${var1};"))
+
+        self.assertRenderResult(
+            root, """
+int var5 = 2;
+int var4 = 1;
+int var3 = var4;
+int var2 = var5;
+int var1 = var2 + var3;
+(void)var1;
+        """)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/example.cc.tmpl b/third_party/blink/renderer/bindings/scripts/bind_gen/example.cc.tmpl
deleted file mode 100644
index 2d28b2b..0000000
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/example.cc.tmpl
+++ /dev/null
@@ -1,15 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-
-<%
-   interface = web_idl.find('HTMLElement')
-%>
-
-class ${interface.identifier} : public ${interface.inherited.identifier} {
-  // attributes
-  % for attribute in interface.attributes:
-    int ${attribute.identifier};
-  % endfor
-};
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/example.py b/third_party/blink/renderer/bindings/scripts/bind_gen/example.py
index ec9c639..f33dd24 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/example.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/example.py
@@ -4,16 +4,44 @@
 
 import os.path
 
-from .mako_renderer import MakoRenderer
+from . import code_node
 from .clang_format import clang_format
+from .mako_renderer import MakoRenderer
 
 
 def run_example(web_idl_database, output_dirs):
-    mako = MakoRenderer()
-    mako_output = mako.render('example.cc.tmpl', web_idl=web_idl_database)
+    renderer = MakoRenderer()
 
     filename = 'v8_example.cc'
-    formatted_result = clang_format(mako_output, filename=filename)
+    filepath = os.path.join(output_dirs['core'], filename)
 
-    with open(os.path.join(output_dirs['core'], filename), 'w') as output_file:
-        output_file.write(formatted_result.contents)
+    root_node = code_node.SequenceNode(renderer=renderer)
+    root_node.extend([
+        code_node.SimpleNode(template_text="${z} = ${x} + ${y};"),
+        code_node.SimpleNode(template_text="print ${z};"),
+    ])
+
+    def make_symbol(name, template_text):
+        def constructor(symbol_node):
+            return code_node.SymbolDefinitionNode(
+                symbol_node, template_text=template_text)
+
+        return code_node.SymbolNode(
+            name=name, definition_node_constructor=constructor)
+
+    root_node.add_template_vars({
+        'x': make_symbol('x', "int ${x} = 1;"),
+        'y': make_symbol('y', "int ${y} = 2;"),
+        'z': make_symbol('z', "int ${z};"),
+    })
+
+    prev = ''
+    current = str(root_node)
+    while current != prev:
+        prev = current
+        current = str(root_node)
+    rendered_text = current
+
+    format_result = clang_format(rendered_text, filename=filename)
+    with open(filepath, 'w') as output_file:
+        output_file.write(format_result.contents)
diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py b/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py
index 1f2a75e..793dde1 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/mako_renderer.py
@@ -2,18 +2,67 @@
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
 
-import os.path
-
-from mako.lookup import TemplateLookup
-
-_TEMPLATES_DIR = os.path.dirname(__file__)
+import mako.lookup
+import mako.template
 
 
 class MakoRenderer(object):
-    def __init__(self, template_dirs=None):
-        template_dirs = template_dirs or [_TEMPLATES_DIR]
-        self._template_lookup = TemplateLookup(directories=template_dirs)
+    """Represents a renderer object implemented with Mako templates."""
 
-    def render(self, template_path, **variable_bindings):
-        template = self._template_lookup.get_template(template_path)
-        return template.render(**variable_bindings)
+    def __init__(self, template_dirs=None):
+        template_dirs = template_dirs or []
+        self._template_lookup = mako.lookup.TemplateLookup(
+            directories=template_dirs)
+
+        self._caller_stack = []
+
+    def render(self,
+               caller,
+               template_path=None,
+               template_text=None,
+               template_vars=None):
+        """
+        Renders the template with variable bindings.
+
+        It's okay to invoke |render| method recursively and |caller| is pushed
+        onto the call stack, which is accessible via |callers| and |last_caller|
+        methods.
+
+        Args:
+            template_path: A filepath to a template file.
+            template_text: A text content to be used as a template.  Either of
+                |template_path| or |template_text| must be specified.
+            template_vars: Template variable bindings.
+            caller: An object to be pushed onto the call stack.
+        """
+
+        assert not (template_path is None and template_text is None)
+        assert not (template_path and template_text)
+        assert isinstance(template_vars, dict)
+        assert caller is not None
+
+        self._caller_stack.append(caller)
+
+        try:
+            if template_path:
+                template = self._template_lookup.get_template(template_path)
+            elif template_text:
+                template = mako.template.Template(text=template_text)
+            text = template.render(**template_vars)
+        finally:
+            self._caller_stack.pop()
+
+        return text
+
+    @property
+    def callers(self):
+        """
+        Returns the callers of this renderer in the order from the last caller
+        to the first caller.
+        """
+        return reversed(self._caller_stack)
+
+    @property
+    def last_caller(self):
+        """Returns the last caller in the call stack of this renderer."""
+        return self._caller_stack[-1]
diff --git a/third_party/blink/renderer/bindings/scripts/generate_bindings.pydeps b/third_party/blink/renderer/bindings/scripts/generate_bindings.pydeps
index ac914942..28c6e59e 100644
--- a/third_party/blink/renderer/bindings/scripts/generate_bindings.pydeps
+++ b/third_party/blink/renderer/bindings/scripts/generate_bindings.pydeps
@@ -21,6 +21,7 @@
 ../../build/scripts/blinkbuild/name_style_converter.py
 bind_gen/__init__.py
 bind_gen/clang_format.py
+bind_gen/code_node.py
 bind_gen/example.py
 bind_gen/mako_renderer.py
 generate_bindings.py
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
index 6184b53..cdd0bd1 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.cc
@@ -647,7 +647,7 @@
 
 void LocalFrameClientImpl::DownloadURL(
     const ResourceRequest& request,
-    DownloadCrossOriginRedirects cross_origin_redirect_behavior) {
+    network::mojom::RedirectMode cross_origin_redirect_behavior) {
   if (!web_frame_->Client())
     return;
   DCHECK(web_frame_->GetFrame()->GetDocument());
@@ -656,11 +656,9 @@
     web_frame_->GetFrame()->GetDocument()->GetPublicURLManager().Resolve(
         request.Url(), blob_url_token.InitWithNewPipeAndPassReceiver());
   }
-  web_frame_->Client()->DownloadURL(
-      WrappedResourceRequest(request),
-      static_cast<WebLocalFrameClient::CrossOriginRedirects>(
-          cross_origin_redirect_behavior),
-      blob_url_token.PassPipe());
+  web_frame_->Client()->DownloadURL(WrappedResourceRequest(request),
+                                    cross_origin_redirect_behavior,
+                                    blob_url_token.PassPipe());
 }
 
 bool LocalFrameClientImpl::NavigateBackForward(int offset) const {
@@ -1252,9 +1250,4 @@
   return web_frame_->Client()->EvictFromBackForwardCache();
 }
 
-STATIC_ASSERT_ENUM(DownloadCrossOriginRedirects::kFollow,
-                   WebLocalFrameClient::CrossOriginRedirects::kFollow);
-STATIC_ASSERT_ENUM(DownloadCrossOriginRedirects::kNavigate,
-                   WebLocalFrameClient::CrossOriginRedirects::kNavigate);
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/exported/local_frame_client_impl.h b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
index c3918d5..f87c0dd 100644
--- a/third_party/blink/renderer/core/exported/local_frame_client_impl.h
+++ b/third_party/blink/renderer/core/exported/local_frame_client_impl.h
@@ -141,7 +141,7 @@
   void ProgressEstimateChanged(double progress_estimate) override;
   void ForwardResourceTimingToParent(const WebResourceTimingInfo&) override;
   void DownloadURL(const ResourceRequest&,
-                   DownloadCrossOriginRedirects) override;
+                   network::mojom::RedirectMode) override;
   bool NavigateBackForward(int offset) const override;
   void DidAccessInitialDocument() override;
   void DidDisplayInsecureContent() override;
diff --git a/third_party/blink/renderer/core/frame/local_frame_client.h b/third_party/blink/renderer/core/frame/local_frame_client.h
index d2db9b3..cb92ea9 100644
--- a/third_party/blink/renderer/core/frame/local_frame_client.h
+++ b/third_party/blink/renderer/core/frame/local_frame_client.h
@@ -119,10 +119,6 @@
 struct WebScrollIntoViewParams;
 class WebTextCheckClient;
 
-// Whether to follow cross origin redirects when downloading, or treating
-// the download as a navigation instead.
-enum class DownloadCrossOriginRedirects { kFollow, kNavigate };
-
 class CORE_EXPORT LocalFrameClient : public FrameClient {
  public:
   ~LocalFrameClient() override = default;
@@ -190,7 +186,7 @@
   virtual void ForwardResourceTimingToParent(const WebResourceTimingInfo&) = 0;
 
   virtual void DownloadURL(const ResourceRequest&,
-                           DownloadCrossOriginRedirects) = 0;
+                           network::mojom::RedirectMode) = 0;
 
   virtual bool NavigateBackForward(int offset) const = 0;
 
diff --git a/third_party/blink/renderer/core/frame/local_frame_view.cc b/third_party/blink/renderer/core/frame/local_frame_view.cc
index 937308fd..3dd437fe 100644
--- a/third_party/blink/renderer/core/frame/local_frame_view.cc
+++ b/third_party/blink/renderer/core/frame/local_frame_view.cc
@@ -198,18 +198,16 @@
 // flash between navigations. The delay should be small enough so that it won't
 // confuse users expecting a new page to appear after navigation and the omnibar
 // has updated the url display.
-constexpr int kAvoidFlashCommitDelayDefaultInMs = 500;  // 30 frames @ 60hz
-constexpr char kAvoidFlashCommitDelayParameterName[] = "commit_delay";
+constexpr int kPaintHoldingCommitDelayDefaultInMs = 500;  // 30 frames @ 60hz
+constexpr char kPaintHoldingCommitDelayParameterName[] = "commit_delay";
 
-// Get the field trial parameter value for AvoidFlashBetweenNavigation.
-base::TimeDelta GetCommitDelayForAvoidFlashBetweenNavigation() {
-  DCHECK(base::FeatureList::IsEnabled(
-      blink::features::kAvoidFlashBetweenNavigation));
+// Get the field trial parameter value for Paint Holding.
+base::TimeDelta GetCommitDelayForPaintHolding() {
+  DCHECK(base::FeatureList::IsEnabled(blink::features::kPaintHolding));
   return base::TimeDelta::FromMilliseconds(
       base::GetFieldTrialParamByFeatureAsInt(
-          blink::features::kAvoidFlashBetweenNavigation,
-          kAvoidFlashCommitDelayParameterName,
-          kAvoidFlashCommitDelayDefaultInMs));
+          blink::features::kPaintHolding, kPaintHoldingCommitDelayParameterName,
+          kPaintHoldingCommitDelayDefaultInMs));
 }
 
 }  // namespace
@@ -4170,12 +4168,11 @@
   // and we do not want to defer a second time if we have already done
   // so once and resumed commits already.
   if (document &&
-      base::FeatureList::IsEnabled(
-          blink::features::kAvoidFlashBetweenNavigation) &&
+      base::FeatureList::IsEnabled(blink::features::kPaintHolding) &&
       document->DeferredCompositorCommitIsAllowed() &&
       !have_deferred_commits_) {
-    chrome_client.StartDeferringCommits(
-        GetFrame(), GetCommitDelayForAvoidFlashBetweenNavigation());
+    chrome_client.StartDeferringCommits(GetFrame(),
+                                        GetCommitDelayForPaintHolding());
     have_deferred_commits_ = true;
   }
 
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc b/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc
index 0ddd7b0..f8fa995 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_font_cache_test.cc
@@ -23,7 +23,7 @@
   void SetUp() override;
 
   HTMLCanvasElement& CanvasElement() const { return *canvas_element_; }
-  CanvasRenderingContext* Context2d() const;
+  CanvasRenderingContext* Context2D() const;
   CanvasFontCache* Cache() { return GetDocument().GetCanvasFontCache(); }
 
  private:
@@ -32,7 +32,7 @@
 
 CanvasFontCacheTest::CanvasFontCacheTest() = default;
 
-CanvasRenderingContext* CanvasFontCacheTest::Context2d() const {
+CanvasRenderingContext* CanvasFontCacheTest::Context2D() const {
   // If the following check fails, perhaps you forgot to call createContext
   // in your test?
   EXPECT_NE(nullptr, CanvasElement().RenderingContext());
@@ -50,7 +50,7 @@
   CanvasContextCreationAttributesCore attributes;
   attributes.alpha = true;
   canvas_element_->GetCanvasRenderingContext(canvas_type, attributes);
-  Context2d();  // Calling this for the checks
+  Context2D();  // Calling this for the checks
 }
 
 TEST_F(CanvasFontCacheTest, CacheHardLimit) {
@@ -58,7 +58,7 @@
   unsigned i;
   for (i = 0; i < Cache()->HardMaxFonts() + 1; i++) {
     font_string = String::Number(i + 1) + "px sans-serif";
-    Context2d()->setFont(font_string);
+    Context2D()->setFont(font_string);
     if (i < Cache()->HardMaxFonts()) {
       EXPECT_TRUE(Cache()->IsInCache("1px sans-serif"));
     } else {
@@ -69,22 +69,22 @@
 }
 
 TEST_F(CanvasFontCacheTest, PageVisibilityChange) {
-  Context2d()->setFont("10px sans-serif");
+  Context2D()->setFont("10px sans-serif");
   EXPECT_TRUE(Cache()->IsInCache("10px sans-serif"));
   GetPage().SetIsHidden(/*is_hidden=*/true, /*initial_state=*/false);
   EXPECT_FALSE(Cache()->IsInCache("10px sans-serif"));
 
-  Context2d()->setFont("15px sans-serif");
+  Context2D()->setFont("15px sans-serif");
   EXPECT_FALSE(Cache()->IsInCache("10px sans-serif"));
   EXPECT_TRUE(Cache()->IsInCache("15px sans-serif"));
 
-  Context2d()->setFont("10px sans-serif");
+  Context2D()->setFont("10px sans-serif");
   EXPECT_TRUE(Cache()->IsInCache("10px sans-serif"));
   EXPECT_FALSE(Cache()->IsInCache("15px sans-serif"));
 
   GetPage().SetIsHidden(/*is_hidden=*/false, /*initial_state=*/false);
-  Context2d()->setFont("15px sans-serif");
-  Context2d()->setFont("10px sans-serif");
+  Context2D()->setFont("15px sans-serif");
+  Context2D()->setFont("10px sans-serif");
   EXPECT_TRUE(Cache()->IsInCache("10px sans-serif"));
   EXPECT_TRUE(Cache()->IsInCache("15px sans-serif"));
 }
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
index 1d6f032..9f30981 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.cc
@@ -136,7 +136,7 @@
 CanvasRenderingContext::ContextType CanvasRenderingContext::ContextTypeFromId(
     const String& id) {
   if (id == "2d")
-    return kContext2d;
+    return kContext2D;
   if (id == "experimental-webgl")
     return kContextExperimentalWebgl;
   if (id == "webgl")
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
index 4985dc7..165e8f9 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h
@@ -65,7 +65,7 @@
   enum ContextType {
     // Do not change assigned numbers of existing items: add new features to the
     // end of the list.
-    kContext2d = 0,
+    kContext2D = 0,
     kContextExperimentalWebgl = 2,
     kContextWebgl = 3,
     kContextWebgl2 = 4,
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
index 65ad86e..8b51ddf 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
+++ b/third_party/blink/renderer/core/html/canvas/canvas_rendering_context_host.cc
@@ -226,6 +226,12 @@
     return ScriptPromise();
   }
 
+  // It's possible that there are recorded commands that have not been resolved
+  // Finalize frame will be called in GetImage, but if there's no
+  // resourceProvider yet then the IsPaintable check will fail
+  if (RenderingContext())
+    RenderingContext()->FinalizeFrame();
+
   if (!this->IsPaintable() || Size().IsEmpty()) {
     error_msg << "The size of " << object_name << " is zero.";
     exception_state.ThrowDOMException(DOMExceptionCode::kIndexSizeError,
@@ -240,8 +246,6 @@
     return ScriptPromise();
   }
 
-  // It's possible that there are recorded commands that have not been resolved
-  RenderingContext()->FinalizeFrame();
   base::TimeTicks start_time = base::TimeTicks::Now();
   scoped_refptr<StaticBitmapImage> image_bitmap =
       RenderingContext()->GetImage(kPreferNoAcceleration);
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.cc b/third_party/blink/renderer/core/html/forms/range_input_type.cc
index cae012a..05c46a7 100644
--- a/third_party/blink/renderer/core/html/forms/range_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/range_input_type.cc
@@ -415,4 +415,8 @@
   return closest_left;
 }
 
+void RangeInputType::ValueAttributeChanged() {
+  UpdateView();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/html/forms/range_input_type.h b/third_party/blink/renderer/core/html/forms/range_input_type.h
index 1ff7566..b4da9bc 100644
--- a/third_party/blink/renderer/core/html/forms/range_input_type.h
+++ b/third_party/blink/renderer/core/html/forms/range_input_type.h
@@ -85,6 +85,7 @@
 
   // InputTypeView function:
   void UpdateView() override;
+  void ValueAttributeChanged() override;
 
   bool tick_mark_values_dirty_;
   Vector<Decimal> tick_mark_values_;
diff --git a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
index ef55cd7..816a88d 100644
--- a/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
+++ b/third_party/blink/renderer/core/html/forms/text_field_input_type.cc
@@ -375,8 +375,8 @@
 }
 
 void TextFieldInputType::AttributeChanged() {
-  // FIXME: Updating on any attribute update should be unnecessary. We should
-  // figure out what attributes affect.
+  // TODO(crbug.com/1012774): Updating on any attribute update should be
+  // unnecessary. We should figure out what attributes affect.
   UpdateView();
 }
 
diff --git a/third_party/blink/renderer/core/html/html_anchor_element.cc b/third_party/blink/renderer/core/html/html_anchor_element.cc
index 93801116..2a47c9e0 100644
--- a/third_party/blink/renderer/core/html/html_anchor_element.cc
+++ b/third_party/blink/renderer/core/html/html_anchor_element.cc
@@ -433,7 +433,7 @@
     request.SetRequestContext(mojom::RequestContextType::DOWNLOAD);
     request.SetRequestorOrigin(GetDocument().GetSecurityOrigin());
     frame->Client()->DownloadURL(request,
-                                 DownloadCrossOriginRedirects::kNavigate);
+                                 network::mojom::RedirectMode::kManual);
     return;
   }
 
diff --git a/third_party/blink/renderer/core/layout/layout_block.cc b/third_party/blink/renderer/core/layout/layout_block.cc
index be2c95e..92d0ae4 100644
--- a/third_party/blink/renderer/core/layout/layout_block.cc
+++ b/third_party/blink/renderer/core/layout/layout_block.cc
@@ -1496,7 +1496,8 @@
   const ComputedStyle& style_to_use = StyleRef();
   if (!IsTableCell() && style_to_use.LogicalWidth().IsFixed() &&
       style_to_use.LogicalWidth().Value() >= 0 &&
-      !(IsDeprecatedFlexItem() && !style_to_use.LogicalWidth().IntValue()))
+      !(IsFlexItemCommon() && Parent()->StyleRef().IsDeprecatedWebkitBox() &&
+        !style_to_use.LogicalWidth().IntValue()))
     min_preferred_logical_width_ = max_preferred_logical_width_ =
         AdjustContentBoxLogicalWidthForBoxSizing(
             LayoutUnit(style_to_use.LogicalWidth().Value()));
diff --git a/third_party/blink/renderer/core/layout/layout_box.cc b/third_party/blink/renderer/core/layout/layout_box.cc
index 2c59485..4ac3f76 100644
--- a/third_party/blink/renderer/core/layout/layout_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_box.cc
@@ -3188,7 +3188,7 @@
 
 bool LayoutBox::IsStretchingColumnFlexItem() const {
   LayoutObject* parent = Parent();
-  if (parent->IsDeprecatedFlexibleBox() &&
+  if (parent->StyleRef().IsDeprecatedWebkitBox() &&
       parent->StyleRef().BoxOrient() == EBoxOrient::kVertical &&
       parent->StyleRef().BoxAlign() == EBoxAlignment::kStretch)
     return true;
diff --git a/third_party/blink/renderer/core/layout/layout_box.h b/third_party/blink/renderer/core/layout/layout_box.h
index fe0ef2b..87a70c1 100644
--- a/third_party/blink/renderer/core/layout/layout_box.h
+++ b/third_party/blink/renderer/core/layout/layout_box.h
@@ -1298,9 +1298,6 @@
   bool IsCustomItem() const;
   bool IsCustomItemShrinkToFit() const;
 
-  bool IsDeprecatedFlexItem() const {
-    return IsFlexItemCommon() && Parent()->IsDeprecatedFlexibleBox();
-  }
   bool IsFlexItemIncludingDeprecatedAndNG() const {
     return IsFlexItemCommon() &&
            Parent()->IsFlexibleBoxIncludingDeprecatedAndNG();
diff --git a/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc b/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
index 4ee5291..856dba9 100644
--- a/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
+++ b/third_party/blink/renderer/core/layout/layout_deprecated_flexible_box.cc
@@ -433,7 +433,7 @@
     TextAutosizer::LayoutScope text_autosizer_layout_scope(this);
 
     if (previous_size != Size() ||
-        (Parent()->IsDeprecatedFlexibleBox() &&
+        (Parent()->StyleRef().IsDeprecatedWebkitBox() &&
          Parent()->StyleRef().BoxOrient() == EBoxOrient::kHorizontal &&
          Parent()->StyleRef().BoxAlign() == EBoxAlignment::kStretch))
       relayout_children = true;
diff --git a/third_party/blink/renderer/core/loader/empty_clients.h b/third_party/blink/renderer/core/loader/empty_clients.h
index d8f84747..d3097bd1 100644
--- a/third_party/blink/renderer/core/loader/empty_clients.h
+++ b/third_party/blink/renderer/core/loader/empty_clients.h
@@ -320,7 +320,7 @@
   void ForwardResourceTimingToParent(const WebResourceTimingInfo&) override {}
 
   void DownloadURL(const ResourceRequest&,
-                   DownloadCrossOriginRedirects) override {}
+                   network::mojom::RedirectMode) override {}
 
   DocumentLoader* CreateDocumentLoader(
       LocalFrame*,
diff --git a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
index 7f774ab..56961f4 100644
--- a/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
+++ b/third_party/blink/renderer/core/offscreencanvas/offscreen_canvas.cc
@@ -215,6 +215,8 @@
     EventTarget&,
     base::Optional<IntRect> crop_rect,
     const ImageBitmapOptions* options) {
+  if (context_)
+    context_->FinalizeFrame();
   return ImageBitmapSource::FulfillImageBitmap(
       script_state,
       IsPaintable() ? ImageBitmap::Create(this, crop_rect, options) : nullptr);
diff --git a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
index a09dba6..109bf48 100644
--- a/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/elements/elements_strings.grdp
@@ -270,9 +270,6 @@
   <message name="IDS_DEVTOOLS_f750c8807bc5f4a7bd259788114a4ebd" desc="Tooltip text that appears when hovering over the largeicon background color button in the Styles Sidebar Pane of the Elements panel">
     Add background-color
   </message>
-  <message name="IDS_DEVTOOLS_f80bc338b6146b566004a046f8137c85" desc="Text in Event Listeners Widget of the Elements panel">
-    Passive
-  </message>
   <message name="IDS_DEVTOOLS_f8d0f1896a9e4ad16d43caa28534df32" desc="Show all button text content in Styles Sidebar Pane of the Elements panel">
     Show All Properties (<ph name="PROPERTIES_LENGTH___COUNT">$1s<ex>3</ex></ph> more)
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js b/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
index 816012f..ba5f6e09 100644
--- a/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
+++ b/third_party/blink/renderer/devtools/front_end/elements_test_runner/ElementsTestRunner.js
@@ -148,6 +148,15 @@
   }
 };
 
+/**
+ * @param {!EventListeners.EventListenersView} eventListenersView
+ * @param {boolean=} force
+ * @return {!Promise}
+ */
+ElementsTestRunner.expandAndDumpEventListenersPromise = function(eventListenersView, force) {
+  return new Promise(resolve => ElementsTestRunner.expandAndDumpEventListeners(eventListenersView, resolve, force));
+};
+
 ElementsTestRunner.inlineStyleSection = function() {
   return UI.panels.elements._stylesWidget._sectionBlocks[0].sections[0];
 };
diff --git a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
index 1d2bfd8..7022c9c41 100644
--- a/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
+++ b/third_party/blink/renderer/devtools/front_end/event_listeners/EventListenersView.js
@@ -303,7 +303,8 @@
   _setTitle(object, linkifier) {
     const title = this.listItemElement.createChild('span', 'event-listener-details');
     const subtitle = this.listItemElement.createChild('span', 'event-listener-tree-subtitle');
-    subtitle.appendChild(linkifier.linkifyRawLocation(this._eventListener.location(), this._eventListener.sourceURL()));
+    const linkElement = linkifier.linkifyRawLocation(this._eventListener.location(), this._eventListener.sourceURL());
+    subtitle.appendChild(linkElement);
 
     this._valueTitle =
         ObjectUI.ObjectPropertiesSection.createValueElement(object, false /* wasThrown */, false /* showPreview */);
@@ -313,7 +314,10 @@
       const deleteButton = title.createChild('span', 'event-listener-button');
       deleteButton.textContent = Common.UIString('Remove');
       deleteButton.title = Common.UIString('Delete event listener');
-      deleteButton.addEventListener('click', removeListener.bind(this), false);
+      deleteButton.addEventListener('click', event => {
+        this._removeListener();
+        event.consume();
+      }, false);
       title.appendChild(deleteButton);
     }
 
@@ -321,28 +325,34 @@
       const passiveButton = title.createChild('span', 'event-listener-button');
       passiveButton.textContent = Common.UIString('Toggle Passive');
       passiveButton.title = Common.UIString('Toggle whether event listener is passive or blocking');
-      passiveButton.addEventListener('click', togglePassiveListener.bind(this), false);
+      passiveButton.addEventListener('click', event => {
+        this._togglePassiveListener();
+        event.consume();
+      }, false);
       title.appendChild(passiveButton);
     }
 
-    /**
-     * @param {!Event} event
-     * @this {EventListeners.ObjectEventListenerBar}
-     */
-    function removeListener(event) {
-      event.consume();
-      this._removeListenerBar();
-      this._eventListener.remove();
-    }
+    this.listItemElement.addEventListener('contextmenu', event => {
+      const menu = new UI.ContextMenu(event);
+      if (event.target !== linkElement) {
+        menu.appendApplicableItems(linkElement);
+      }
+      menu.defaultSection().appendItem(
+          ls`Delete event listener`, this._removeListener.bind(this), !this._eventListener.canRemove());
+      menu.defaultSection().appendCheckboxItem(
+          ls`Passive`, this._togglePassiveListener.bind(this), this._eventListener.passive(),
+          !this._eventListener.canTogglePassive());
+      menu.show();
+    });
+  }
 
-    /**
-     * @param {!Event} event
-     * @this {EventListeners.ObjectEventListenerBar}
-     */
-    function togglePassiveListener(event) {
-      event.consume();
-      this._eventListener.togglePassive().then(this._changeCallback());
-    }
+  _removeListener() {
+    this._removeListenerBar();
+    this._eventListener.remove();
+  }
+
+  _togglePassiveListener() {
+    this._eventListener.togglePassive().then(this._changeCallback());
   }
 
   _removeListenerBar() {
diff --git a/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp b/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
index aaca2e29..981cbae 100644
--- a/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
+++ b/third_party/blink/renderer/devtools/front_end/langpacks/shared_strings.grdp
@@ -616,6 +616,9 @@
   <message name="IDS_DEVTOOLS_f53944c3a55bdb5ad65c6226e358a626" desc="Text to undock the DevTools">
     Undock into separate window
   </message>
+  <message name="IDS_DEVTOOLS_f80bc338b6146b566004a046f8137c85" desc="Text in Event Listeners Widget of the Elements panel">
+    Passive
+  </message>
   <message name="IDS_DEVTOOLS_f907e651164789346ae0a1e257c462d8" desc="Label for a group of JavaScript files">
     Script
   </message>
diff --git a/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js b/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
index 31b3873d..37758fe 100644
--- a/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
+++ b/third_party/blink/renderer/devtools/front_end/object_ui/ObjectPropertiesSection.js
@@ -469,6 +469,17 @@
 /** @const */
 ObjectUI.ObjectPropertiesSection._maxRenderableStringLength = 10000;
 
+ObjectUI.ObjectPropertiesSectionsTreeOutline = class extends UI.TreeOutlineInShadow {
+  constructor() {
+    super();
+    this.registerRequiredCSS('object_ui/objectValue.css');
+    this.registerRequiredCSS('object_ui/objectPropertiesSection.css');
+    this._editable = true;
+    this.contentElement.classList.add('source-code');
+    this.contentElement.classList.add('object-properties-section');
+    this.hideOverflow();
+  }
+};
 
 /**
  * @unrestricted
@@ -797,6 +808,18 @@
 
   /**
    * @override
+   * @return {boolean}
+   */
+  onenter() {
+    if (!this.property.value.customPreview() && (this.property.writable || this.property.setter)) {
+      this._startEditing();
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * @override
    */
   onattach() {
     this.update();
@@ -981,6 +1004,7 @@
     this._updateExpandable();
     this.listItemElement.scrollLeft = 0;
     this.listItemElement.classList.remove('editing-sub-part');
+    this.select();
   }
 
   _editingCancelled() {
@@ -1429,7 +1453,7 @@
     section.addEventListener(UI.TreeOutline.Events.ElementAttached, this._elementAttached, this);
     section.addEventListener(UI.TreeOutline.Events.ElementExpanded, this._elementExpanded, this);
     section.addEventListener(UI.TreeOutline.Events.ElementCollapsed, this._elementCollapsed, this);
-    section[ObjectUI.ObjectPropertiesSectionExpandController._treeOutlineId] = id;
+    section[ObjectUI.ObjectPropertiesSectionsTreeExpandController._treeOutlineId] = id;
 
     if (this._expandedProperties.has(id)) {
       section.expand();
@@ -1478,7 +1502,7 @@
    * @return {string}
    */
   _propertyPath(treeElement) {
-    const cachedPropertyPath = treeElement[ObjectUI.ObjectPropertiesSectionExpandController._cachedPathSymbol];
+    const cachedPropertyPath = treeElement[ObjectUI.ObjectPropertiesSectionsTreeExpandController._cachedPathSymbol];
     if (cachedPropertyPath) {
       return cachedPropertyPath;
     }
@@ -1499,15 +1523,114 @@
       result = currentName + (result ? '.' + result : '');
       current = current.parent;
     }
-    const treeOutlineId = treeElement.treeOutline[ObjectUI.ObjectPropertiesSectionExpandController._treeOutlineId];
+    const treeOutlineId = treeElement.treeOutline[ObjectUI.ObjectPropertiesSectionsTreeExpandController._treeOutlineId];
     result = treeOutlineId + (result ? ':' + result : '');
-    treeElement[ObjectUI.ObjectPropertiesSectionExpandController._cachedPathSymbol] = result;
+    treeElement[ObjectUI.ObjectPropertiesSectionsTreeExpandController._cachedPathSymbol] = result;
     return result;
   }
 };
 
-ObjectUI.ObjectPropertiesSectionExpandController._cachedPathSymbol = Symbol('cachedPath');
-ObjectUI.ObjectPropertiesSectionExpandController._treeOutlineId = Symbol('treeOutlineId');
+/**
+ * @unrestricted
+ */
+ObjectUI.ObjectPropertiesSectionsTreeExpandController = class {
+  /**
+   * @param {!UI.TreeOutline} treeOutline
+   */
+  constructor(treeOutline) {
+    /** @type {!Set.<string>} */
+    this._expandedProperties = new Set();
+    treeOutline.addEventListener(UI.TreeOutline.Events.ElementAttached, this._elementAttached, this);
+    treeOutline.addEventListener(UI.TreeOutline.Events.ElementExpanded, this._elementExpanded, this);
+    treeOutline.addEventListener(UI.TreeOutline.Events.ElementCollapsed, this._elementCollapsed, this);
+  }
+
+  /**
+   * @param {string} id
+   * @param {!ObjectUI.ObjectPropertiesSection.RootElement} section
+   */
+  watchSection(id, section) {
+    section[ObjectUI.ObjectPropertiesSectionsTreeExpandController._treeOutlineId] = id;
+
+    if (this._expandedProperties.has(id)) {
+      section.expand();
+    }
+  }
+
+  /**
+   * @param {string} id
+   */
+  stopWatchSectionsWithId(id) {
+    for (const property of this._expandedProperties) {
+      if (property.startsWith(id + ':')) {
+        this._expandedProperties.delete(property);
+      }
+    }
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _elementAttached(event) {
+    const element = /** @type {!UI.TreeElement} */ (event.data);
+    if (element.isExpandable() && this._expandedProperties.has(this._propertyPath(element))) {
+      element.expand();
+    }
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _elementExpanded(event) {
+    const element = /** @type {!UI.TreeElement} */ (event.data);
+    this._expandedProperties.add(this._propertyPath(element));
+  }
+
+  /**
+   * @param {!Common.Event} event
+   */
+  _elementCollapsed(event) {
+    const element = /** @type {!UI.TreeElement} */ (event.data);
+    this._expandedProperties.delete(this._propertyPath(element));
+  }
+
+  /**
+   * @param {!UI.TreeElement} treeElement
+   * @return {string}
+   */
+  _propertyPath(treeElement) {
+    const cachedPropertyPath = treeElement[ObjectUI.ObjectPropertiesSectionsTreeExpandController._cachedPathSymbol];
+    if (cachedPropertyPath) {
+      return cachedPropertyPath;
+    }
+
+    let current = treeElement;
+    let sectionRoot = current;
+    const rootElement = treeElement.treeOutline.rootElement();
+
+    let result;
+
+    while (current !== rootElement) {
+      let currentName = '';
+      if (current.property) {
+        currentName = current.property.name;
+      } else {
+        currentName = typeof current.title === 'string' ? current.title : current.title.textContent;
+      }
+
+      result = currentName + (result ? '.' + result : '');
+      sectionRoot = current;
+      current = current.parent;
+    }
+    const treeOutlineId = sectionRoot[ObjectUI.ObjectPropertiesSectionsTreeExpandController._treeOutlineId];
+    result = treeOutlineId + (result ? ':' + result : '');
+    treeElement[ObjectUI.ObjectPropertiesSectionsTreeExpandController._cachedPathSymbol] = result;
+    return result;
+  }
+};
+
+ObjectUI.ObjectPropertiesSectionsTreeExpandController._cachedPathSymbol = Symbol('cachedPath');
+ObjectUI.ObjectPropertiesSectionsTreeExpandController._treeOutlineId = Symbol('treeOutlineId');
 
 /**
  * @implements {UI.Renderer}
diff --git a/third_party/blink/renderer/modules/BUILD.gn b/third_party/blink/renderer/modules/BUILD.gn
index ca57079..0044f51 100644
--- a/third_party/blink/renderer/modules/BUILD.gn
+++ b/third_party/blink/renderer/modules/BUILD.gn
@@ -389,6 +389,7 @@
     "peerconnection/rtc_quic_transport_test.cc",
     "peerconnection/rtc_quic_transport_test.h",
     "peerconnection/rtc_sctp_transport_test.cc",
+    "peerconnection/webrtc_media_stream_track_adapter_test.cc",
     "picture_in_picture/picture_in_picture_controller_test.cc",
     "presentation/mock_presentation_service.h",
     "presentation/presentation_availability_state_test.cc",
diff --git a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
index 3c53dd8..6d75c3e 100644
--- a/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
+++ b/third_party/blink/renderer/modules/accessibility/ax_relation_cache.cc
@@ -290,7 +290,6 @@
 
 void AXRelationCache::LabelChanged(Node* node) {
   const auto& id = To<HTMLElement>(node)->FastGetAttribute(kForAttr);
-  LOG(ERROR) << "LabelChanged " << id;
   if (!id.IsEmpty()) {
     all_previously_seen_label_target_ids_.insert(id);
     if (auto* control = To<HTMLLabelElement>(node)->control())
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
index 3f35851b..39680ab 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d.h
@@ -87,7 +87,7 @@
           static_cast<HTMLCanvasElement*>(host), attrs);
     }
     CanvasRenderingContext::ContextType GetContextType() const override {
-      return CanvasRenderingContext::kContext2d;
+      return CanvasRenderingContext::kContext2D;
     }
 
    private:
@@ -237,7 +237,7 @@
   void UpdateElementAccessibility(const Path&, Element*);
 
   CanvasRenderingContext::ContextType GetContextType() const override {
-    return CanvasRenderingContext::kContext2d;
+    return CanvasRenderingContext::kContext2D;
   }
 
   String ColorSpaceAsString() const override;
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
index 9e7f31e..f7f5fd9 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_api_test.cc
@@ -32,7 +32,7 @@
   void SetUp() override;
 
   HTMLCanvasElement& CanvasElement() const { return *canvas_element_; }
-  CanvasRenderingContext2D* Context2d() const;
+  CanvasRenderingContext2D* Context2D() const;
 
   void CreateContext(OpacityMode);
 
@@ -42,7 +42,7 @@
 
 CanvasRenderingContext2DAPITest::CanvasRenderingContext2DAPITest() = default;
 
-CanvasRenderingContext2D* CanvasRenderingContext2DAPITest::Context2d() const {
+CanvasRenderingContext2D* CanvasRenderingContext2DAPITest::Context2D() const {
   // If the following check fails, perhaps you forgot to call createContext
   // in your test?
   EXPECT_NE(nullptr, CanvasElement().RenderingContext());
@@ -56,7 +56,7 @@
   CanvasContextCreationAttributesCore attributes;
   attributes.alpha = opacity_mode == kNonOpaque;
   canvas_element_->GetCanvasRenderingContext(canvas_type, attributes);
-  Context2d();  // Calling this for the checks
+  Context2D();  // Calling this for the checks
 }
 
 void CanvasRenderingContext2DAPITest::SetUp() {
@@ -70,13 +70,13 @@
 TEST_F(CanvasRenderingContext2DAPITest, SetShadowColor_Clamping) {
   CreateContext(kNonOpaque);
 
-  Context2d()->setShadowColor("rgba(0,0,0,0)");
-  EXPECT_EQ(String("rgba(0, 0, 0, 0)"), Context2d()->shadowColor());
-  Context2d()->setShadowColor("rgb(0,0,0)");
-  EXPECT_EQ(String("#000000"), Context2d()->shadowColor());
-  Context2d()->setShadowColor("rgb(0,999,0)");
-  EXPECT_EQ(String("#00ff00"), Context2d()->shadowColor());
-  Context2d()->setShadowColor(
+  Context2D()->setShadowColor("rgba(0,0,0,0)");
+  EXPECT_EQ(String("rgba(0, 0, 0, 0)"), Context2D()->shadowColor());
+  Context2D()->setShadowColor("rgb(0,0,0)");
+  EXPECT_EQ(String("#000000"), Context2D()->shadowColor());
+  Context2D()->setShadowColor("rgb(0,999,0)");
+  EXPECT_EQ(String("#00ff00"), Context2D()->shadowColor());
+  Context2D()->setShadowColor(
       "rgb(0,"
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "999999999999999999999999999999999999999999999999999999999999999999999999"
@@ -86,23 +86,23 @@
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       ",0)");
-  EXPECT_EQ(String("#00ff00"), Context2d()->shadowColor());
-  Context2d()->setShadowColor("rgb(0,0,256)");
-  EXPECT_EQ(String("#0000ff"), Context2d()->shadowColor());
-  Context2d()->setShadowColor(
+  EXPECT_EQ(String("#00ff00"), Context2D()->shadowColor());
+  Context2D()->setShadowColor("rgb(0,0,256)");
+  EXPECT_EQ(String("#0000ff"), Context2D()->shadowColor());
+  Context2D()->setShadowColor(
       "rgb(999999999999999999999999,0,-9999999999999999999999999999)");
-  EXPECT_EQ(String("#ff0000"), Context2d()->shadowColor());
-  Context2d()->setShadowColor(
+  EXPECT_EQ(String("#ff0000"), Context2D()->shadowColor());
+  Context2D()->setShadowColor(
       "rgba("
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "9999999999,9,0,1)");
-  EXPECT_EQ(String("#ff0900"), Context2d()->shadowColor());
-  Context2d()->setShadowColor(
+  EXPECT_EQ(String("#ff0900"), Context2D()->shadowColor());
+  Context2D()->setShadowColor(
       "rgba("
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "9999999999,9,0,-99999999999999999999999999999999999999)");
-  EXPECT_EQ(String("rgba(255, 9, 0, 0)"), Context2d()->shadowColor());
-  Context2d()->setShadowColor(
+  EXPECT_EQ(String("rgba(255, 9, 0, 0)"), Context2D()->shadowColor());
+  Context2D()->setShadowColor(
       "rgba(7,"
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "9999999999,0,"
@@ -114,8 +114,8 @@
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "99999999999999999)");
-  EXPECT_EQ(String("#07ff00"), Context2d()->shadowColor());
-  Context2d()->setShadowColor(
+  EXPECT_EQ(String("#07ff00"), Context2D()->shadowColor());
+  Context2D()->setShadowColor(
       "rgba(-7,"
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "9999999999,0,"
@@ -127,9 +127,9 @@
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "999999999999999999999999999999999999999999999999999999999999999999999999"
       "99999999999999999)");
-  EXPECT_EQ(String("#00ff00"), Context2d()->shadowColor());
-  Context2d()->setShadowColor("rgba(0%,100%,0%,0.4)");
-  EXPECT_EQ(String("rgba(0, 255, 0, 0.4)"), Context2d()->shadowColor());
+  EXPECT_EQ(String("#00ff00"), Context2D()->shadowColor());
+  Context2D()->setShadowColor("rgba(0%,100%,0%,0.4)");
+  EXPECT_EQ(String("rgba(0, 255, 0, 0.4)"), Context2D()->shadowColor());
 }
 
 String TrySettingStrokeStyle(CanvasRenderingContext2D* ctx,
@@ -173,15 +173,15 @@
 TEST_F(CanvasRenderingContext2DAPITest, ColorSerialization) {
   CreateContext(kNonOpaque);
   // Check round trips
-  TrySettingColor(Context2d(), "transparent", "rgba(0, 0, 0, 0)");
-  TrySettingColor(Context2d(), "red", "#ff0000");
-  TrySettingColor(Context2d(), "white", "#ffffff");
-  TrySettingColor(Context2d(), "", "#666666");
-  TrySettingColor(Context2d(), "RGBA(0, 0, 0, 0)", "rgba(0, 0, 0, 0)");
-  TrySettingColor(Context2d(), "rgba(0,255,0,1.0)", "#00ff00");
-  TrySettingColor(Context2d(), "rgba(1,2,3,0.4)", "rgba(1, 2, 3, 0.4)");
-  TrySettingColor(Context2d(), "RgB(1,2,3)", "#010203");
-  TrySettingColor(Context2d(), "rGbA(1,2,3,0)", "rgba(1, 2, 3, 0)");
+  TrySettingColor(Context2D(), "transparent", "rgba(0, 0, 0, 0)");
+  TrySettingColor(Context2D(), "red", "#ff0000");
+  TrySettingColor(Context2D(), "white", "#ffffff");
+  TrySettingColor(Context2D(), "", "#666666");
+  TrySettingColor(Context2D(), "RGBA(0, 0, 0, 0)", "rgba(0, 0, 0, 0)");
+  TrySettingColor(Context2D(), "rgba(0,255,0,1.0)", "#00ff00");
+  TrySettingColor(Context2D(), "rgba(1,2,3,0.4)", "rgba(1, 2, 3, 0.4)");
+  TrySettingColor(Context2D(), "RgB(1,2,3)", "#010203");
+  TrySettingColor(Context2D(), "rGbA(1,2,3,0)", "rgba(1, 2, 3, 0)");
 }
 
 TEST_F(CanvasRenderingContext2DAPITest, DefaultAttributeValues) {
@@ -189,19 +189,19 @@
 
   {
     StringOrCanvasGradientOrCanvasPattern value;
-    Context2d()->strokeStyle(value);
+    Context2D()->strokeStyle(value);
     EXPECT_TRUE(value.IsString());
     EXPECT_EQ(String("#000000"), value.GetAsString());
   }
 
   {
     StringOrCanvasGradientOrCanvasPattern value;
-    Context2d()->fillStyle(value);
+    Context2D()->fillStyle(value);
     EXPECT_TRUE(value.IsString());
     EXPECT_EQ(String("#000000"), value.GetAsString());
   }
 
-  EXPECT_EQ(String("rgba(0, 0, 0, 0)"), Context2d()->shadowColor());
+  EXPECT_EQ(String("rgba(0, 0, 0, 0)"), Context2D()->shadowColor());
 }
 
 TEST_F(CanvasRenderingContext2DAPITest, LineDashStateSave) {
@@ -211,14 +211,14 @@
   simple_dash.push_back(4);
   simple_dash.push_back(2);
 
-  Context2d()->setLineDash(simple_dash);
-  EXPECT_EQ(simple_dash, Context2d()->getLineDash());
-  Context2d()->save();
+  Context2D()->setLineDash(simple_dash);
+  EXPECT_EQ(simple_dash, Context2D()->getLineDash());
+  Context2D()->save();
   // Realize the save.
-  Context2d()->scale(2, 2);
-  EXPECT_EQ(simple_dash, Context2d()->getLineDash());
-  Context2d()->restore();
-  EXPECT_EQ(simple_dash, Context2d()->getLineDash());
+  Context2D()->scale(2, 2);
+  EXPECT_EQ(simple_dash, Context2D()->getLineDash());
+  Context2D()->restore();
+  EXPECT_EQ(simple_dash, Context2D()->getLineDash());
 }
 
 TEST_F(CanvasRenderingContext2DAPITest, CreateImageData) {
@@ -228,7 +228,7 @@
 
   // create a 100x50 imagedata and fill it with white pixels
   ImageData* image_data =
-      Context2d()->createImageData(100, 50, exception_state);
+      Context2D()->createImageData(100, 50, exception_state);
   EXPECT_FALSE(exception_state.HadException());
   EXPECT_EQ(100, image_data->width());
   EXPECT_EQ(50, image_data->height());
@@ -242,7 +242,7 @@
   // as 'imageData' but filled with transparent black
 
   ImageData* same_size_image_data =
-      Context2d()->createImageData(image_data, exception_state);
+      Context2D()->createImageData(image_data, exception_state);
   EXPECT_FALSE(exception_state.HadException());
   EXPECT_EQ(100, same_size_image_data->width());
   EXPECT_EQ(50, same_size_image_data->height());
@@ -251,13 +251,13 @@
   // createImageData(width, height) takes the absolute magnitude of the size
   // arguments
 
-  ImageData* imgdata1 = Context2d()->createImageData(10, 20, exception_state);
+  ImageData* imgdata1 = Context2D()->createImageData(10, 20, exception_state);
   EXPECT_FALSE(exception_state.HadException());
-  ImageData* imgdata2 = Context2d()->createImageData(-10, 20, exception_state);
+  ImageData* imgdata2 = Context2D()->createImageData(-10, 20, exception_state);
   EXPECT_FALSE(exception_state.HadException());
-  ImageData* imgdata3 = Context2d()->createImageData(10, -20, exception_state);
+  ImageData* imgdata3 = Context2D()->createImageData(10, -20, exception_state);
   EXPECT_FALSE(exception_state.HadException());
-  ImageData* imgdata4 = Context2d()->createImageData(-10, -20, exception_state);
+  ImageData* imgdata4 = Context2D()->createImageData(-10, -20, exception_state);
   EXPECT_FALSE(exception_state.HadException());
 
   EXPECT_EQ((unsigned)800, imgdata1->data()->length());
@@ -270,7 +270,7 @@
   CreateContext(kNonOpaque);
   DummyExceptionStateForTesting exception_state;
   ImageData* too_big_image_data =
-      Context2d()->createImageData(1000000, 1000000, exception_state);
+      Context2D()->createImageData(1000000, 1000000, exception_state);
   EXPECT_EQ(nullptr, too_big_image_data);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kRangeError, exception_state.CodeAs<ESErrorType>());
@@ -280,7 +280,7 @@
   CreateContext(kNonOpaque);
   DummyExceptionStateForTesting exception_state;
   ImageData* image_data =
-      Context2d()->getImageData(0, 0, 1000000, 1000000, exception_state);
+      Context2D()->getImageData(0, 0, 1000000, 1000000, exception_state);
   EXPECT_EQ(nullptr, image_data);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kRangeError, exception_state.CodeAs<ESErrorType>());
@@ -290,14 +290,14 @@
        GetImageDataIntegerOverflowNegativeParams) {
   CreateContext(kNonOpaque);
   DummyExceptionStateForTesting exception_state;
-  ImageData* image_data = Context2d()->getImageData(
+  ImageData* image_data = Context2D()->getImageData(
       1, -2147483647, 1, -2147483647, exception_state);
   EXPECT_EQ(nullptr, image_data);
   EXPECT_TRUE(exception_state.HadException());
   EXPECT_EQ(ESErrorType::kRangeError, exception_state.CodeAs<ESErrorType>());
 
   exception_state.ClearException();
-  image_data = Context2d()->getImageData(-2147483647, 1, -2147483647, 1,
+  image_data = Context2D()->getImageData(-2147483647, 1, -2147483647, 1,
                                          exception_state);
   EXPECT_EQ(nullptr, image_data);
   EXPECT_TRUE(exception_state.HadException());
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
index 3a7cbe98..b670fe7 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.cc
@@ -628,7 +628,7 @@
          FillStyle()->GetCanvasPattern()->GetPattern();
 }
 
-// Only to be used if the CanvasRenderingContext2dState has Pattern
+// Only to be used if the CanvasRenderingContext2DState has Pattern
 bool CanvasRenderingContext2DState::PatternIsAccelerated() const {
   DCHECK(HasPattern());
   return FillStyle()->GetCanvasPattern()->GetPattern()->IsTextureBacked();
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
index ba1967c..eaf5b213 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_state.h
@@ -119,7 +119,7 @@
 
   bool HasPattern() const;
 
-  // Only to be used if the CanvasRenderingContext2dState has Pattern
+  // Only to be used if the CanvasRenderingContext2DState has Pattern
   bool PatternIsAccelerated() const;
 
   enum Direction { kDirectionInherit, kDirectionRTL, kDirectionLTR };
diff --git a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
index a63454be..4a33e8c 100644
--- a/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
+++ b/third_party/blink/renderer/modules/canvas/canvas2d/canvas_rendering_context_2d_test.cc
@@ -110,14 +110,14 @@
   bool IsCanvasResourceHostSet(Canvas2DLayerBridge* bridge) {
     return !!bridge->resource_host_;
   }
-  CanvasRenderingContext2D* Context2d() const {
+  CanvasRenderingContext2D* Context2D() const {
     return static_cast<CanvasRenderingContext2D*>(
         CanvasElement().RenderingContext());
   }
   void DrawSomething() {
     CanvasElement().DidDraw();
     CanvasElement().PreFinalizeFrame();
-    Context2d()->FinalizeFrame();
+    Context2D()->FinalizeFrame();
     CanvasElement().PostFinalizeFrame();
     // Grabbing an image forces a flush
     CanvasElement().Snapshot(kBackBuffer, kPreferAcceleration);
@@ -345,43 +345,43 @@
   CanvasElement().SetResourceProviderForTesting(                               \
       nullptr, std::move(mock_surface), size);                                 \
   EXPECT_CALL(*surface_ptr, WillOverwriteCanvas()).Times(EXPECTED_OVERDRAWS);  \
-  Context2d()->save();
+  Context2D()->save();
 
 #define TEST_OVERDRAW_FINALIZE \
-  Context2d()->restore();      \
+  Context2D()->restore();      \
   Mock::VerifyAndClearExpectations(surface_ptr);
 
 #define TEST_OVERDRAW_1(EXPECTED_OVERDRAWS, CALL1) \
   do {                                             \
     TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS)        \
-    Context2d()->CALL1;                            \
+    Context2D()->CALL1;                            \
     TEST_OVERDRAW_FINALIZE                         \
   } while (0)
 
 #define TEST_OVERDRAW_2(EXPECTED_OVERDRAWS, CALL1, CALL2) \
   do {                                                    \
     TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS)               \
-    Context2d()->CALL1;                                   \
-    Context2d()->CALL2;                                   \
+    Context2D()->CALL1;                                   \
+    Context2D()->CALL2;                                   \
     TEST_OVERDRAW_FINALIZE                                \
   } while (0)
 
 #define TEST_OVERDRAW_3(EXPECTED_OVERDRAWS, CALL1, CALL2, CALL3) \
   do {                                                           \
     TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS)                      \
-    Context2d()->CALL1;                                          \
-    Context2d()->CALL2;                                          \
-    Context2d()->CALL3;                                          \
+    Context2D()->CALL1;                                          \
+    Context2D()->CALL2;                                          \
+    Context2D()->CALL3;                                          \
     TEST_OVERDRAW_FINALIZE                                       \
   } while (0)
 
 #define TEST_OVERDRAW_4(EXPECTED_OVERDRAWS, CALL1, CALL2, CALL3, CALL4) \
   do {                                                                  \
     TEST_OVERDRAW_SETUP(EXPECTED_OVERDRAWS)                             \
-    Context2d()->CALL1;                                                 \
-    Context2d()->CALL2;                                                 \
-    Context2d()->CALL3;                                                 \
-    Context2d()->CALL4;                                                 \
+    Context2D()->CALL1;                                                 \
+    Context2D()->CALL2;                                                 \
+    Context2D()->CALL3;                                                 \
+    Context2D()->CALL4;                                                 \
     TEST_OVERDRAW_FINALIZE                                              \
   } while (0)
 
@@ -639,14 +639,14 @@
 
 TEST_F(CanvasRenderingContext2DTest, CanvasDisposedBeforeContext) {
   CreateContext(kNonOpaque);
-  Context2d()->fillRect(0, 0, 1, 1);  // results in task observer registration
+  Context2D()->fillRect(0, 0, 1, 1);  // results in task observer registration
 
-  Context2d()->DetachHost();
+  Context2D()->DetachHost();
 
   // This is the only method that is callable after DetachHost
   // Test passes by not crashing.
   base::PendingTask dummy_pending_task(FROM_HERE, base::Closure());
-  Context2d()->DidProcessTask(dummy_pending_task);
+  Context2D()->DidProcessTask(dummy_pending_task);
 
   // Test passes by not crashing during teardown
 }
@@ -686,7 +686,7 @@
       size, CanvasColorParams(), kPreferAcceleration);
   CanvasElement().SetResourceProviderForTesting(
       nullptr, std::move(fake_accelerate_surface), size);
-  CanvasRenderingContext2D* context = Context2d();
+  CanvasRenderingContext2D* context = Context2D();
 
   // 800 = 10 * 10 * 4 * 2 where 10*10 is canvas size, 4 is num of bytes per
   // pixel per buffer, and 2 is an estimate of num of gpu buffers required
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
index 124f261..d89fb6e 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h"
 
 #include "base/metrics/histogram_functions.h"
+#include "third_party/blink/public/common/features.h"
 #include "third_party/blink/renderer/bindings/modules/v8/offscreen_rendering_context.h"
 #include "third_party/blink/renderer/core/css/offscreen_font_selector.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser.h"
@@ -19,6 +20,7 @@
 #include "third_party/blink/renderer/platform/graphics/canvas_resource_provider.h"
 #include "third_party/blink/renderer/platform/graphics/graphics_types.h"
 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
+#include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
 #include "third_party/blink/renderer/platform/heap/heap.h"
 #include "third_party/blink/renderer/platform/text/bidi_text_run.h"
@@ -79,8 +81,26 @@
     OffscreenCanvas* canvas,
     const CanvasContextCreationAttributesCore& attrs)
     : CanvasRenderingContext(canvas, attrs),
+      is_deferral_enabled_(
+          base::FeatureList::IsEnabled(features::kCanvasAlwaysDeferral)),
       random_generator_((uint32_t)base::RandUint64()),
       bernoulli_distribution_(kUMASampleProbability) {
+  is_valid_size_ = IsValidImageSize(Host()->Size());
+
+  if (is_deferral_enabled_) {
+    StartRecording();
+
+    // Clear the background transparent or opaque. Similar code at
+    // CanvasResourceProvider::Clear().
+    if (IsCanvas2DBufferValid()) {
+      DCHECK(recorder_);
+      recorder_->getRecordingCanvas()->clear(
+          ColorParams().GetOpacityMode() == kOpaque ? SK_ColorBLACK
+                                                    : SK_ColorTRANSPARENT);
+      DidDraw();
+    }
+  }
+
   ExecutionContext* execution_context = canvas->GetTopExecutionContext();
   if (auto* document = DynamicTo<Document>(execution_context)) {
     Settings* settings = document->GetSettings();
@@ -104,10 +124,50 @@
   // TODO(fserb): consolidate this with PushFrame
   SkIRect damage_rect(dirty_rect_for_commit_);
   dirty_rect_for_commit_.setEmpty();
+  FinalizeFrame();
   Host()->Commit(ProduceCanvasResource(), damage_rect);
   GetOffscreenFontCache().PruneLocalFontCache(kMaxCachedFonts);
 }
 
+void OffscreenCanvasRenderingContext2D::StartRecording() {
+  DCHECK(is_deferral_enabled_);
+  recorder_ = std::make_unique<PaintRecorder>();
+
+  cc::PaintCanvas* canvas = recorder_->beginRecording(Width(), Height());
+  // Always save an initial frame, to support resetting the top level matrix
+  // and clip.
+  canvas->save();
+
+  RestoreMatrixClipStack(canvas);
+}
+
+void OffscreenCanvasRenderingContext2D::FlushRecording() {
+  if (!have_recorded_draw_commands_)
+    return;
+
+  {  // Make a new scope so that PaintRecord gets deleted and that gets timed
+    CanvasResourceProvider* resource_provider = GetCanvasResourceProvider();
+    cc::PaintCanvas* canvas = resource_provider->Canvas();
+    canvas->drawPicture(recorder_->finishRecordingAsPicture());
+    resource_provider->FlushSkia();
+  }
+  GetCanvasResourceProvider()->ReleaseLockedImages();
+
+  if (is_deferral_enabled_)
+    StartRecording();
+  have_recorded_draw_commands_ = false;
+}
+
+void OffscreenCanvasRenderingContext2D::FinalizeFrame() {
+  TRACE_EVENT0("blink", "OffscreenCanvasRenderingContext2D::FinalizeFrame");
+
+  // Make sure surface is ready for painting: fix the rendering mode now
+  // because it will be too late during the paint invalidation phase.
+  if (!GetOrCreateCanvasResourceProvider())
+    return;
+  FlushRecording();
+}
+
 // BaseRenderingContext2D implementation
 bool OffscreenCanvasRenderingContext2D::OriginClean() const {
   return Host()->OriginClean();
@@ -134,7 +194,13 @@
     const {
   if (!Host() || Host()->Size().IsEmpty())
     return false;
-  return !!offscreenCanvasForBinding()->GetOrCreateResourceProvider();
+  return !!GetOrCreateCanvasResourceProvider();
+}
+
+CanvasResourceProvider*
+OffscreenCanvasRenderingContext2D::GetOrCreateCanvasResourceProvider() const {
+  // TODO(aaronhk) use Host() instead of offscreenCanvasForBinding() here
+  return offscreenCanvasForBinding()->GetOrCreateResourceProvider();
 }
 
 CanvasResourceProvider*
@@ -144,11 +210,15 @@
 void OffscreenCanvasRenderingContext2D::Reset() {
   Host()->DiscardResourceProvider();
   BaseRenderingContext2D::Reset();
+  if (is_deferral_enabled_)
+    StartRecording();
+  // Because the host may have changed to a zero size
+  is_valid_size_ = IsValidImageSize(Host()->Size());
 }
 
 scoped_refptr<CanvasResource>
 OffscreenCanvasRenderingContext2D::ProduceCanvasResource() {
-  if (!CanCreateCanvas2dResourceProvider())
+  if (!GetOrCreateCanvasResourceProvider())
     return nullptr;
   scoped_refptr<CanvasResource> frame =
       GetCanvasResourceProvider()->ProduceCanvasResource();
@@ -164,6 +234,7 @@
     return false;
 
   SkIRect damage_rect(dirty_rect_for_commit_);
+  FinalizeFrame();
   bool ret = Host()->PushFrame(ProduceCanvasResource(), damage_rect);
   dirty_rect_for_commit_.setEmpty();
   GetOffscreenFontCache().PruneLocalFontCache(kMaxCachedFonts);
@@ -175,10 +246,9 @@
   WebFeature feature = WebFeature::kOffscreenCanvasTransferToImageBitmap2D;
   UseCounter::Count(ExecutionContext::From(script_state), feature);
 
-  if (!CanCreateCanvas2dResourceProvider())
+  if (!GetOrCreateCanvasResourceProvider())
     return nullptr;
-  scoped_refptr<StaticBitmapImage> image =
-      GetCanvasResourceProvider()->Snapshot();
+  scoped_refptr<StaticBitmapImage> image = GetImage(kPreferAcceleration);
   if (!image)
     return nullptr;
   image->SetOriginClean(this->OriginClean());
@@ -193,12 +263,21 @@
       return nullptr;
     }
   }
-  Host()->DiscardResourceProvider();  // "Transfer" means no retained buffer.
+
+  // "Transfer" means no retained buffer. Matrix transformations need to be
+  // preserved though.
+  Host()->DiscardResourceProvider();
+  if (is_deferral_enabled_) {
+    recorder_->getRecordingCanvas()->restore();
+    recorder_->getRecordingCanvas()->save();
+  }
+
   return ImageBitmap::Create(std::move(image));
 }
 
 scoped_refptr<StaticBitmapImage> OffscreenCanvasRenderingContext2D::GetImage(
     AccelerationHint hint) {
+  FinalizeFrame();
   if (!IsPaintable())
     return nullptr;
   scoped_refptr<StaticBitmapImage> image =
@@ -219,6 +298,10 @@
 }
 
 cc::PaintCanvas* OffscreenCanvasRenderingContext2D::DrawingCanvas() const {
+  if (!is_valid_size_)
+    return nullptr;
+  if (is_deferral_enabled_)
+    return recorder_->getRecordingCanvas();
   if (!CanCreateCanvas2dResourceProvider())
     return nullptr;
   return GetCanvasResourceProvider()->Canvas();
@@ -226,17 +309,25 @@
 
 cc::PaintCanvas* OffscreenCanvasRenderingContext2D::ExistingDrawingCanvas()
     const {
+  if (!is_valid_size_)
+    return nullptr;
+  if (is_deferral_enabled_)
+    return recorder_->getRecordingCanvas();
   if (!IsPaintable())
     return nullptr;
   return GetCanvasResourceProvider()->Canvas();
 }
 
 void OffscreenCanvasRenderingContext2D::DidDraw() {
+  if (is_deferral_enabled_)
+    have_recorded_draw_commands_ = true;
   Host()->DidDraw();
   dirty_rect_for_commit_.setWH(Width(), Height());
 }
 
 void OffscreenCanvasRenderingContext2D::DidDraw(const SkIRect& dirty_rect) {
+  if (is_deferral_enabled_)
+    have_recorded_draw_commands_ = true;
   dirty_rect_for_commit_.join(dirty_rect);
   Host()->DidDraw(SkRect::Make(dirty_rect_for_commit_));
 }
@@ -289,6 +380,17 @@
     int x,
     int y) {
   DCHECK(IsPaintable());
+
+  FinalizeFrame();
+  // WritePixels is not supported by deferral. Since we are directly rendering,
+  // we can't do deferral on top of the canvas. Disable deferral completely.
+  is_deferral_enabled_ = false;
+  have_recorded_draw_commands_ = false;
+  recorder_.reset();
+  // install the current matrix/clip stack onto the immediate canvas
+  if (GetOrCreateCanvasResourceProvider())
+    RestoreMatrixClipStack(GetCanvasResourceProvider()->Canvas());
+
   return offscreenCanvasForBinding()->ResourceProvider()->WritePixels(
       orig_info, pixels, row_bytes, x, y);
 }
@@ -519,8 +621,7 @@
 
   Draw(
       [&font, &text_run_paint_info, &location](
-          cc::PaintCanvas* c, const PaintFlags* flags)  // draw lambda
-      {
+          cc::PaintCanvas* c, const PaintFlags* flags) /* draw lambda */ {
         font.DrawBidiText(c, text_run_paint_info, location,
                           Font::kUseFallbackIfFontNotReady, kCDeviceScaleFactor,
                           *flags);
diff --git a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
index 4c89898..9b29eb3 100644
--- a/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
+++ b/third_party/blink/renderer/modules/canvas/offscreencanvas2d/offscreen_canvas_rendering_context_2d.h
@@ -12,6 +12,7 @@
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context.h"
 #include "third_party/blink/renderer/core/html/canvas/canvas_rendering_context_factory.h"
 #include "third_party/blink/renderer/modules/canvas/canvas2d/base_rendering_context_2d.h"
+#include "third_party/blink/renderer/platform/graphics/paint/paint_recorder.h"
 
 namespace blink {
 
@@ -40,7 +41,7 @@
     }
 
     CanvasRenderingContext::ContextType GetContextType() const override {
-      return CanvasRenderingContext::kContext2d;
+      return CanvasRenderingContext::kContext2D;
     }
   };
 
@@ -57,7 +58,7 @@
 
   // CanvasRenderingContext implementation
   ~OffscreenCanvasRenderingContext2D() override;
-  ContextType GetContextType() const override { return kContext2d; }
+  ContextType GetContextType() const override { return kContext2D; }
   bool Is2d() const override { return true; }
   bool IsComposited() const override { return false; }
   bool IsAccelerated() const override;
@@ -95,6 +96,7 @@
   int Height() const final;
 
   bool CanCreateCanvas2dResourceProvider() const final;
+  CanvasResourceProvider* GetOrCreateCanvasResourceProvider() const;
   CanvasResourceProvider* GetCanvasResourceProvider() const;
 
   bool ParseColorOrCurrentColor(Color&, const String& color_string) const final;
@@ -120,6 +122,8 @@
 
   bool PushFrame() override;
 
+  bool HasRecordedDrawCommands() { return have_recorded_draw_commands_; }
+
  protected:
   void NeedsFinalizeFrame() override {
     CanvasRenderingContext::NeedsFinalizeFrame();
@@ -132,6 +136,13 @@
                    int y) override;
 
  private:
+  void StartRecording();
+  bool is_deferral_enabled_;
+  std::unique_ptr<PaintRecorder> recorder_;
+  bool have_recorded_draw_commands_;
+  void FinalizeFrame() final;
+  void FlushRecording();
+
   bool IsPaintable() const final;
   bool IsCanvas2DBufferValid() const override;
 
@@ -148,6 +159,8 @@
   CanvasPixelFormat PixelFormat() const override;
   SkIRect dirty_rect_for_commit_;
 
+  bool is_valid_size_ = false;
+
   std::mt19937 random_generator_;
   std::bernoulli_distribution bernoulli_distribution_;
 };
diff --git a/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc b/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
index 934b99f..687c766 100644
--- a/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
+++ b/third_party/blink/renderer/modules/media_controls/elements/media_control_download_button_element.cc
@@ -79,7 +79,7 @@
     request.SetRequestContext(mojom::RequestContextType::DOWNLOAD);
     request.SetRequestorOrigin(SecurityOrigin::Create(GetDocument().Url()));
     GetDocument().GetFrame()->Client()->DownloadURL(
-        request, DownloadCrossOriginRedirects::kFollow);
+        request, network::mojom::RedirectMode::kError);
   }
   MediaControlInputElement::DefaultEventHandler(event);
 }
diff --git a/third_party/blink/renderer/modules/peerconnection/BUILD.gn b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
index 6f02e49..10ec29be 100644
--- a/third_party/blink/renderer/modules/peerconnection/BUILD.gn
+++ b/third_party/blink/renderer/modules/peerconnection/BUILD.gn
@@ -136,3 +136,21 @@
     "//third_party/webrtc/api:scoped_refptr",
   ]
 }
+
+jumbo_source_set("test_support") {
+  testonly = true
+
+  sources = [
+    "mock_data_channel_impl.cc",
+    "mock_peer_connection_dependency_factory.cc",
+    "mock_peer_connection_impl.cc",
+  ]
+
+  deps = [
+    "//base",
+    "//base/test:test_support",
+    "//testing/gmock",
+    "//third_party/blink/public:test_headers",
+    "//third_party/webrtc_overrides:init_webrtc",
+  ]
+}
diff --git a/third_party/blink/renderer/modules/peerconnection/DEPS b/third_party/blink/renderer/modules/peerconnection/DEPS
index 3256f78..f206842 100644
--- a/third_party/blink/renderer/modules/peerconnection/DEPS
+++ b/third_party/blink/renderer/modules/peerconnection/DEPS
@@ -21,7 +21,7 @@
 ]
 
 specific_include_rules = {
-    "rtc_data_channel_test\.cc" : [
+    ".*test\.cc" : [
         "+base/run_loop.h",
     ],
 }
diff --git a/content/renderer/media/webrtc/mock_data_channel_impl.cc b/third_party/blink/renderer/modules/peerconnection/mock_data_channel_impl.cc
similarity index 67%
rename from content/renderer/media/webrtc/mock_data_channel_impl.cc
rename to third_party/blink/renderer/modules/peerconnection/mock_data_channel_impl.cc
index 1d3547f5..45ccb313 100644
--- a/content/renderer/media/webrtc/mock_data_channel_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_data_channel_impl.cc
@@ -2,23 +2,21 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/mock_data_channel_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_data_channel_impl.h"
 
 #include "base/logging.h"
 
-namespace content {
+namespace blink {
 
 MockDataChannel::MockDataChannel(const std::string& label,
-                const webrtc::DataChannelInit* config)
+                                 const webrtc::DataChannelInit* config)
     : label_(label),
       reliable_(config->reliable),
       state_(webrtc::DataChannelInterface::kConnecting),
       config_(*config),
-      observer_(nullptr) {
-}
+      observer_(nullptr) {}
 
-MockDataChannel::~MockDataChannel() {
-}
+MockDataChannel::~MockDataChannel() {}
 
 void MockDataChannel::RegisterObserver(webrtc::DataChannelObserver* observer) {
   observer_ = observer;
@@ -28,22 +26,34 @@
   observer_ = nullptr;
 }
 
-std::string MockDataChannel::label() const { return label_; }
+std::string MockDataChannel::label() const {
+  return label_;
+}
 
-bool MockDataChannel::reliable() const { return reliable_; }
+bool MockDataChannel::reliable() const {
+  return reliable_;
+}
 
-bool MockDataChannel::ordered() const { return config_.ordered; }
+bool MockDataChannel::ordered() const {
+  return config_.ordered;
+}
 
-std::string MockDataChannel::protocol() const { return config_.protocol; }
+std::string MockDataChannel::protocol() const {
+  return config_.protocol;
+}
 
-bool MockDataChannel::negotiated() const { return config_.negotiated; }
+bool MockDataChannel::negotiated() const {
+  return config_.negotiated;
+}
 
 int MockDataChannel::id() const {
   NOTIMPLEMENTED();
   return 0;
 }
 
-MockDataChannel::DataState MockDataChannel::state() const { return state_; }
+MockDataChannel::DataState MockDataChannel::state() const {
+  return state_;
+}
 
 uint32_t MockDataChannel::messages_sent() const {
   NOTIMPLEMENTED();
@@ -85,4 +95,4 @@
   return state_ == webrtc::DataChannelInterface::kOpen;
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.cc
similarity index 92%
rename from content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
rename to third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.cc
index 60269a0c..317a323 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_dependency_factory.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_dependency_factory.cc
@@ -2,14 +2,13 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 
 #include <stddef.h>
 
-#include "base/logging.h"
-#include "base/strings/utf_string_conversions.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
+#include "base/single_thread_task_runner.h"
 #include "third_party/blink/public/platform/web_media_stream_track.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 #include "third_party/webrtc/api/media_stream_interface.h"
 #include "third_party/webrtc/api/scoped_refptr.h"
 
@@ -21,15 +20,14 @@
 using webrtc::MediaStreamInterface;
 using webrtc::ObserverInterface;
 using webrtc::SessionDescriptionInterface;
-using webrtc::VideoTrackSourceInterface;
 using webrtc::VideoTrackInterface;
+using webrtc::VideoTrackSourceInterface;
 using webrtc::VideoTrackVector;
 
-namespace content {
+namespace blink {
 
 template <class V>
-static typename V::iterator FindTrack(V* vector,
-                                      const std::string& track_id) {
+static typename V::iterator FindTrack(V* vector, const std::string& track_id) {
   auto it = vector->begin();
   for (; it != vector->end(); ++it) {
     if ((*it)->id() == track_id) {
@@ -219,9 +217,13 @@
   return kVideoKind;
 }
 
-std::string MockWebRtcVideoTrack::id() const { return id_; }
+std::string MockWebRtcVideoTrack::id() const {
+  return id_;
+}
 
-bool MockWebRtcVideoTrack::enabled() const { return enabled_; }
+bool MockWebRtcVideoTrack::enabled() const {
+  return enabled_;
+}
 
 MockWebRtcVideoTrack::TrackState MockWebRtcVideoTrack::state() const {
   return state_;
@@ -251,11 +253,8 @@
 
 class MockSessionDescription : public SessionDescriptionInterface {
  public:
-  MockSessionDescription(const std::string& type,
-                         const std::string& sdp)
-      : type_(type),
-        sdp_(sdp) {
-  }
+  MockSessionDescription(const std::string& type, const std::string& sdp)
+      : type_(type), sdp_(sdp) {}
   ~MockSessionDescription() override {}
   cricket::SessionDescription* description() override {
     NOTIMPLEMENTED();
@@ -303,9 +302,7 @@
   MockIceCandidate(const std::string& sdp_mid,
                    int sdp_mline_index,
                    const std::string& sdp)
-      : sdp_mid_(sdp_mid),
-        sdp_mline_index_(sdp_mline_index),
-        sdp_(sdp) {
+      : sdp_mid_(sdp_mid), sdp_mline_index_(sdp_mline_index), sdp_(sdp) {
     // Assign an valid address to |candidate_| to pass assert in code.
     candidate_.set_address(rtc::SocketAddress("127.0.0.1", 5000));
   }
@@ -359,8 +356,7 @@
     const std::string& id,
     webrtc::VideoTrackSourceInterface* source) {
   scoped_refptr<webrtc::VideoTrackInterface> track(
-      new rtc::RefCountedObject<MockWebRtcVideoTrack>(
-          id, source));
+      new rtc::RefCountedObject<MockWebRtcVideoTrack>(id, source));
   return track;
 }
 
@@ -392,4 +388,4 @@
   fail_to_create_session_description_ = fail;
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/mock_peer_connection_impl.cc b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.cc
similarity index 92%
rename from content/renderer/media/webrtc/mock_peer_connection_impl.cc
rename to third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.cc
index 1e8e2fb..ad7a995d 100644
--- a/content/renderer/media/webrtc/mock_peer_connection_impl.cc
+++ b/third_party/blink/renderer/modules/peerconnection/mock_peer_connection_impl.cc
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "content/renderer/media/webrtc/mock_peer_connection_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_impl.h"
 
 #include <stddef.h>
 
@@ -11,9 +11,9 @@
 
 #include "base/logging.h"
 #include "base/stl_util.h"
-#include "content/renderer/media/webrtc/mock_data_channel_impl.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "third_party/blink/public/platform/modules/peerconnection/webrtc_util.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_data_channel_impl.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/webrtc/api/rtp_receiver_interface.h"
 #include "third_party/webrtc/rtc_base/ref_counted_object.h"
 
@@ -28,7 +28,7 @@
 using webrtc::SessionDescriptionInterface;
 using webrtc::SetSessionDescriptionObserver;
 
-namespace content {
+namespace blink {
 
 class MockStreamCollection : public webrtc::StreamCollectionInterface {
  public:
@@ -61,9 +61,7 @@
     }
     return nullptr;
   }
-  void AddStream(MediaStreamInterface* stream) {
-    streams_.push_back(stream);
-  }
+  void AddStream(MediaStreamInterface* stream) { streams_.push_back(stream); }
   void RemoveStream(MediaStreamInterface* stream) {
     auto it = streams_.begin();
     for (; it != streams_.end(); ++it) {
@@ -78,8 +76,7 @@
   ~MockStreamCollection() override {}
 
  private:
-  typedef std::vector<rtc::scoped_refptr<MediaStreamInterface> >
-      StreamVector;
+  typedef std::vector<rtc::scoped_refptr<MediaStreamInterface>> StreamVector;
   StreamVector streams_;
 };
 
@@ -323,11 +320,13 @@
       getstats_result_(true),
       sdp_mline_index_(-1),
       observer_(observer) {
-  ON_CALL(*this, SetLocalDescription(_, _)).WillByDefault(testing::Invoke(
-      this, &MockPeerConnectionImpl::SetLocalDescriptionWorker));
+  ON_CALL(*this, SetLocalDescription(_, _))
+      .WillByDefault(testing::Invoke(
+          this, &MockPeerConnectionImpl::SetLocalDescriptionWorker));
   // TODO(hbos): Remove once no longer mandatory to implement.
-  ON_CALL(*this, SetRemoteDescription(_, _)).WillByDefault(testing::Invoke(
-      this, &MockPeerConnectionImpl::SetRemoteDescriptionWorker));
+  ON_CALL(*this, SetRemoteDescription(_, _))
+      .WillByDefault(testing::Invoke(
+          this, &MockPeerConnectionImpl::SetRemoteDescriptionWorker));
   ON_CALL(*this, SetRemoteDescriptionForMock(_, _))
       .WillByDefault(testing::Invoke(
           [this](
@@ -403,15 +402,15 @@
 }
 
 rtc::scoped_refptr<webrtc::DataChannelInterface>
-MockPeerConnectionImpl::CreateDataChannel(const std::string& label,
-                      const webrtc::DataChannelInit* config) {
-  return new rtc::RefCountedObject<MockDataChannel>(label, config);
+MockPeerConnectionImpl::CreateDataChannel(
+    const std::string& label,
+    const webrtc::DataChannelInit* config) {
+  return new rtc::RefCountedObject<blink::MockDataChannel>(label, config);
 }
 
-bool MockPeerConnectionImpl::GetStats(
-    webrtc::StatsObserver* observer,
-    webrtc::MediaStreamTrackInterface* track,
-    StatsOutputLevel level) {
+bool MockPeerConnectionImpl::GetStats(webrtc::StatsObserver* observer,
+                                      webrtc::MediaStreamTrackInterface* track,
+                                      StatsOutputLevel level) {
   if (!getstats_result_)
     return false;
 
@@ -530,4 +529,4 @@
   return webrtc::RTCError::OK();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_test.cc
similarity index 90%
rename from content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc
rename to third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_test.cc
index 3005478..ef399fd 100644
--- a/content/renderer/media/webrtc/webrtc_media_stream_track_adapter_unittest.cc
+++ b/third_party/blink/renderer/modules/peerconnection/webrtc_media_stream_track_adapter_test.cc
@@ -7,13 +7,10 @@
 #include <memory>
 
 #include "base/bind.h"
-#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_refptr.h"
 #include "base/run_loop.h"
 #include "base/single_thread_task_runner.h"
 #include "base/synchronization/waitable_event.h"
-#include "base/test/task_environment.h"
-#include "content/child/child_process.h"
-#include "content/renderer/media/webrtc/mock_peer_connection_dependency_factory.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "third_party/blink/public/platform/modules/mediastream/media_stream_audio_source.h"
 #include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
@@ -22,14 +19,16 @@
 #include "third_party/blink/public/platform/web_string.h"
 #include "third_party/blink/public/web/modules/mediastream/media_stream_video_track.h"
 #include "third_party/blink/public/web/modules/mediastream/mock_media_stream_video_source.h"
+#include "third_party/blink/public/web/modules/peerconnection/mock_peer_connection_dependency_factory.h"
 #include "third_party/blink/public/web/web_heap.h"
+#include "third_party/blink/renderer/platform/testing/io_task_runner_testing_platform_support.h"
 
-namespace content {
+namespace blink {
 
 class WebRtcMediaStreamTrackAdapterTest : public ::testing::Test {
  public:
   void SetUp() override {
-    dependency_factory_.reset(new MockPeerConnectionDependencyFactory());
+    dependency_factory_.reset(new blink::MockPeerConnectionDependencyFactory());
     main_thread_ = blink::scheduler::GetSingleThreadTaskRunnerForTesting();
   }
 
@@ -114,12 +113,10 @@
   }
 
  protected:
-  // The TaskEnvironment prevents the ChildProcess from leaking a
-  // ThreadPool.
-  base::test::TaskEnvironment task_environment_;
-  ChildProcess child_process_;
+  ScopedTestingPlatformSupport<IOTaskRunnerTestingPlatformSupport> platform_;
 
-  std::unique_ptr<MockPeerConnectionDependencyFactory> dependency_factory_;
+  std::unique_ptr<blink::MockPeerConnectionDependencyFactory>
+      dependency_factory_;
   scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
   scoped_refptr<blink::WebRtcMediaStreamTrackAdapter> track_adapter_;
 };
@@ -164,8 +161,8 @@
 }
 
 TEST_F(WebRtcMediaStreamTrackAdapterTest, RemoteAudioTrack) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("remote_audio_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("remote_audio_track");
   dependency_factory_->GetWebRtcSignalingThread()->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -189,8 +186,8 @@
 }
 
 TEST_F(WebRtcMediaStreamTrackAdapterTest, RemoteVideoTrack) {
-  scoped_refptr<MockWebRtcVideoTrack> webrtc_track =
-      MockWebRtcVideoTrack::Create("remote_video_track");
+  scoped_refptr<blink::MockWebRtcVideoTrack> webrtc_track =
+      blink::MockWebRtcVideoTrack::Create("remote_video_track");
   dependency_factory_->GetWebRtcSignalingThread()->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -214,8 +211,8 @@
 }
 
 TEST_F(WebRtcMediaStreamTrackAdapterTest, RemoteTrackExplicitlyInitialized) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("remote_audio_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("remote_audio_track");
   dependency_factory_->GetWebRtcSignalingThread()->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -243,8 +240,8 @@
 }
 
 TEST_F(WebRtcMediaStreamTrackAdapterTest, LastReferenceOnSignalingThread) {
-  scoped_refptr<MockWebRtcAudioTrack> webrtc_track =
-      MockWebRtcAudioTrack::Create("remote_audio_track");
+  scoped_refptr<blink::MockWebRtcAudioTrack> webrtc_track =
+      blink::MockWebRtcAudioTrack::Create("remote_audio_track");
   dependency_factory_->GetWebRtcSignalingThread()->PostTask(
       FROM_HERE,
       base::BindOnce(
@@ -271,4 +268,4 @@
   RunMessageLoopsUntilIdle();
 }
 
-}  // namespace content
+}  // namespace blink
diff --git a/third_party/blink/renderer/modules/sensor/ambient_light_sensor.cc b/third_party/blink/renderer/modules/sensor/ambient_light_sensor.cc
index 1c7b478c5..1e7b043 100644
--- a/third_party/blink/renderer/modules/sensor/ambient_light_sensor.cc
+++ b/third_party/blink/renderer/modules/sensor/ambient_light_sensor.cc
@@ -42,8 +42,4 @@
   return GetReading().als.value;
 }
 
-void AmbientLightSensor::Trace(blink::Visitor* visitor) {
-  Sensor::Trace(visitor);
-}
-
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/sensor/ambient_light_sensor.h b/third_party/blink/renderer/modules/sensor/ambient_light_sensor.h
index 34b3f1e..45c7757c 100644
--- a/third_party/blink/renderer/modules/sensor/ambient_light_sensor.h
+++ b/third_party/blink/renderer/modules/sensor/ambient_light_sensor.h
@@ -21,8 +21,6 @@
   AmbientLightSensor(ExecutionContext*, const SensorOptions*, ExceptionState&);
 
   double illuminance(bool& is_null) const;
-
-  void Trace(blink::Visitor*) override;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/serial/serial_port.cc b/third_party/blink/renderer/modules/serial/serial_port.cc
index 9fddc35..b03503c 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port.cc
@@ -5,6 +5,7 @@
 #include "third_party/blink/renderer/modules/serial/serial_port.h"
 
 #include "mojo/public/cpp/bindings/pending_remote.h"
+#include "third_party/blink/renderer/bindings/core/v8/script_function.h"
 #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/streams/readable_stream.h"
@@ -74,6 +75,57 @@
   }
 }
 
+// A ScriptFunction that calls ContinueClose() on the provided SerialPort.
+class ContinueCloseFunction : public ScriptFunction {
+ public:
+  static v8::Local<v8::Function> Create(ScriptState* script_state,
+                                        SerialPort* port) {
+    auto* self =
+        MakeGarbageCollected<ContinueCloseFunction>(script_state, port);
+    return self->BindToV8Function();
+  }
+
+  ContinueCloseFunction(ScriptState* script_state, SerialPort* port)
+      : ScriptFunction(script_state), port_(port) {}
+
+  ScriptValue Call(ScriptValue) override {
+    return port_->ContinueClose(GetScriptState()).GetScriptValue();
+  }
+
+  void Trace(Visitor* visitor) override {
+    visitor->Trace(port_);
+    ScriptFunction::Trace(visitor);
+  }
+
+ private:
+  Member<SerialPort> port_;
+};
+
+// A ScriptFunction that calls AbortClose() on the provided SerialPort.
+class AbortCloseFunction : public ScriptFunction {
+ public:
+  static v8::Local<v8::Function> Create(ScriptState* script_state,
+                                        SerialPort* port) {
+    auto* self = MakeGarbageCollected<AbortCloseFunction>(script_state, port);
+    return self->BindToV8Function();
+  }
+
+  AbortCloseFunction(ScriptState* script_state, SerialPort* port)
+      : ScriptFunction(script_state), port_(port) {}
+
+  ScriptValue Call(ScriptValue) override {
+    port_->AbortClose();
+    return ScriptValue();
+  }
+
+  void Trace(Visitor* visitor) override {
+    visitor->Trace(port_);
+    ScriptFunction::Trace(visitor);
+  }
+
+ private:
+  Member<SerialPort> port_;
+};
 }  // namespace
 
 SerialPort::SerialPort(Serial* parent, mojom::blink::SerialPortInfoPtr info)
@@ -200,7 +252,7 @@
   if (readable_)
     return readable_;
 
-  if (!port_ || open_resolver_)
+  if (!port_ || open_resolver_ || closing_)
     return nullptr;
 
   mojo::ScopedDataPipeConsumerHandle readable_pipe;
@@ -221,7 +273,7 @@
   if (writable_)
     return writable_;
 
-  if (!port_ || open_resolver_)
+  if (!port_ || open_resolver_ || closing_)
     return nullptr;
 
   mojo::ScopedDataPipeProducerHandle writable_pipe;
@@ -285,24 +337,65 @@
   return resolver->Promise();
 }
 
-void SerialPort::close() {
-  if (underlying_source_) {
-    // The ReadableStream will report "done" when the data pipe is closed.
-    underlying_source_->ExpectClose();
-    underlying_source_ = nullptr;
-    readable_ = nullptr;
+ScriptPromise SerialPort::close(ScriptState* script_state,
+                                ExceptionState& exception_state) {
+  if (!port_) {
+    exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
+                                      "The port is already closed.");
+    return ScriptPromise();
   }
-  if (underlying_sink_) {
-    // TODO(crbug.com/893334): Rather than triggering an error on the
-    // WritableStream this should imply a call to abort() and fail if the stream
-    // is locked.
-    underlying_sink_->SignalErrorOnClose(DOMExceptionFromSendError(
-        device::mojom::SerialSendError::DISCONNECTED));
-    underlying_sink_ = nullptr;
-    writable_ = nullptr;
+
+  if (closing_) {
+    exception_state.ThrowDOMException(
+        DOMExceptionCode::kInvalidStateError,
+        "A call to close() is already in progress.");
+    return ScriptPromise();
   }
-  port_.reset();
-  client_receiver_.reset();
+
+  closing_ = true;
+
+  HeapVector<ScriptPromise> promises;
+  if (readable_) {
+    promises.push_back(readable_->cancel(script_state, exception_state));
+    if (exception_state.HadException()) {
+      closing_ = false;
+      return ScriptPromise();
+    }
+  }
+  if (writable_) {
+    auto* reason = MakeGarbageCollected<DOMException>(
+        DOMExceptionCode::kInvalidStateError, kPortClosed);
+    promises.push_back(writable_->abort(script_state,
+                                        ScriptValue::From(script_state, reason),
+                                        exception_state));
+    if (exception_state.HadException()) {
+      closing_ = false;
+      return ScriptPromise();
+    }
+  }
+
+  return ScriptPromise::All(script_state, promises)
+      .Then(ContinueCloseFunction::Create(script_state, this),
+            AbortCloseFunction::Create(script_state, this));
+}
+
+ScriptPromise SerialPort::ContinueClose(ScriptState* script_state) {
+  DCHECK(closing_);
+  DCHECK(!readable_);
+  DCHECK(!writable_);
+  DCHECK(!close_resolver_);
+
+  if (!port_)
+    return ScriptPromise::CastUndefined(script_state);
+
+  close_resolver_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
+  port_->Close(WTF::Bind(&SerialPort::OnClose, WrapPersistent(this)));
+  return close_resolver_->Promise();
+}
+
+void SerialPort::AbortClose() {
+  DCHECK(closing_);
+  closing_ = false;
 }
 
 void SerialPort::UnderlyingSourceClosed() {
@@ -328,6 +421,7 @@
   visitor->Trace(underlying_sink_);
   visitor->Trace(open_resolver_);
   visitor->Trace(signal_resolvers_);
+  visitor->Trace(close_resolver_);
   ScriptWrappable::Trace(visitor);
 }
 
@@ -366,6 +460,7 @@
 }
 
 void SerialPort::OnConnectionError() {
+  closing_ = false;
   port_.reset();
   client_receiver_.reset();
 
@@ -378,6 +473,8 @@
   underlying_source_ = nullptr;
   SerialPortUnderlyingSink* underlying_sink = underlying_sink_;
   underlying_sink_ = nullptr;
+  ScriptPromiseResolver* close_resolver = close_resolver_;
+  close_resolver_ = nullptr;
 
   if (open_resolver) {
     open_resolver->Reject(MakeGarbageCollected<DOMException>(
@@ -395,6 +492,8 @@
     underlying_sink->SignalErrorOnClose(
         DOMExceptionFromSendError(SerialSendError::DISCONNECTED));
   }
+  if (close_resolver)
+    close_resolver->Resolve();
 }
 
 void SerialPort::OnOpen(
@@ -487,4 +586,15 @@
   resolver->Resolve();
 }
 
+void SerialPort::OnClose() {
+  DCHECK(close_resolver_);
+  closing_ = false;
+  port_.reset();
+  client_receiver_.reset();
+
+  ScriptPromiseResolver* close_resolver = close_resolver_;
+  close_resolver_ = nullptr;
+  close_resolver->Resolve();
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/serial/serial_port.h b/third_party/blink/renderer/modules/serial/serial_port.h
index 40ee155a..7ead0877 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.h
+++ b/third_party/blink/renderer/modules/serial/serial_port.h
@@ -50,12 +50,14 @@
   ScriptPromise setSignals(ScriptState*,
                            const SerialOutputSignals*,
                            ExceptionState&);
-  void close();
+  ScriptPromise close(ScriptState*, ExceptionState&);
 
   const base::UnguessableToken& token() const { return info_->token; }
 
   void UnderlyingSourceClosed();
   void UnderlyingSinkClosed();
+  ScriptPromise ContinueClose(ScriptState*);
+  void AbortClose();
 
   void ContextDestroyed();
   void Trace(Visitor*) override;
@@ -80,6 +82,7 @@
   void OnGetSignals(ScriptPromiseResolver*,
                     device::mojom::blink::SerialPortControlSignalsPtr);
   void OnSetSignals(ScriptPromiseResolver*, bool success);
+  void OnClose();
 
   mojom::blink::SerialPortInfoPtr info_;
   Member<Serial> parent_;
@@ -93,11 +96,17 @@
   Member<WritableStream> writable_;
   Member<SerialPortUnderlyingSink> underlying_sink_;
 
+  // Indicates that the port is being closed and so the streams should not be
+  // reopened on demand.
+  bool closing_ = false;
+
   // Resolver for the Promise returned by open().
   Member<ScriptPromiseResolver> open_resolver_;
   // Resolvers for the Promises returned by getSignals() and setSignals() to
   // reject them on Mojo connection failure.
   HeapHashSet<Member<ScriptPromiseResolver>> signal_resolvers_;
+  // Resolver for the Promise returned by ClosePort().
+  Member<ScriptPromiseResolver> close_resolver_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/serial/serial_port.idl b/third_party/blink/renderer/modules/serial/serial_port.idl
index 6628cc5..24f88bf 100644
--- a/third_party/blink/renderer/modules/serial/serial_port.idl
+++ b/third_party/blink/renderer/modules/serial/serial_port.idl
@@ -19,6 +19,6 @@
     Promise<SerialInputSignals> getSignals();
     [CallWith=ScriptState, RaisesException]
     Promise<void> setSignals(SerialOutputSignals signals);
-    [MeasureAs=SerialPortClose]
-    void close();
+    [RaisesException, CallWith=ScriptState, MeasureAs=SerialPortClose]
+    Promise<void> close();
 };
diff --git a/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc b/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc
index a2867d7..0c10f77 100644
--- a/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port_underlying_sink.cc
@@ -69,11 +69,11 @@
 
   watcher_.Cancel();
   data_pipe_.reset();
+  serial_port_->UnderlyingSinkClosed();
 
   if (pending_exception_) {
     DOMException* exception = pending_exception_;
     pending_exception_ = nullptr;
-    serial_port_->UnderlyingSinkClosed();
     return ScriptPromise::RejectWithDOMException(script_state, exception);
   }
 
diff --git a/third_party/blink/renderer/modules/serial/serial_port_underlying_source.cc b/third_party/blink/renderer/modules/serial/serial_port_underlying_source.cc
index 8ba9a20..0cf7af149 100644
--- a/third_party/blink/renderer/modules/serial/serial_port_underlying_source.cc
+++ b/third_party/blink/renderer/modules/serial/serial_port_underlying_source.cc
@@ -4,7 +4,6 @@
 
 #include "third_party/blink/renderer/modules/serial/serial_port_underlying_source.h"
 
-#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
 #include "third_party/blink/renderer/core/dom/dom_exception.h"
 #include "third_party/blink/renderer/core/streams/readable_stream_default_controller_interface.h"
 #include "third_party/blink/renderer/core/typed_arrays/dom_typed_array.h"
@@ -28,22 +27,25 @@
 }
 
 ScriptPromise SerialPortUnderlyingSource::pull(ScriptState* script_state) {
-  // Only one pending call to pull() is allowed by the spec.
-  DCHECK(!pending_pull_);
-  // pull() shouldn't be called if an error has been signaled to the controller.
+  // pull() signals that the stream wants more data. By resolving immediately
+  // we allow the stream to be canceled before that data is received. pull()
+  // will not be called again until a chunk is enqueued or if an error has been
+  // signaled to the controller.
   DCHECK(data_pipe_);
 
-  if (ReadData())
-    return ScriptPromise::CastUndefined(script_state);
+  if (!ReadData())
+    ArmWatcher();
 
-  return ArmWatcher(script_state);
+  return ScriptPromise::CastUndefined(script_state);
 }
 
 ScriptPromise SerialPortUnderlyingSource::Cancel(ScriptState* script_state,
                                                  ScriptValue reason) {
-  // TODO(crbug.com/989653): cancel() should trigger a purge of the serial
-  // read buffer.
+  // TODO(crbug.com/989653): Rather than calling Close(), cancel() should
+  // trigger a purge of the serial read buffer and wait for the pipe to close to
+  // indicate the purge has been completed.
   Close();
+  ExpectPipeClose();
   return ScriptPromise::CastUndefined(script_state);
 }
 
@@ -70,19 +72,7 @@
   serial_port_->UnderlyingSourceClosed();
 }
 
-void SerialPortUnderlyingSource::ExpectClose() {
-  if (data_pipe_) {
-    // Pipe is still open. Wait for PipeClosed() to be called.
-    expect_close_ = true;
-    return;
-  }
-
-  Controller()->Close();
-  serial_port_->UnderlyingSourceClosed();
-}
-
 void SerialPortUnderlyingSource::Trace(Visitor* visitor) {
-  visitor->Trace(pending_pull_);
   visitor->Trace(pending_exception_);
   visitor->Trace(serial_port_);
   UnderlyingSourceBase::Trace(visitor);
@@ -113,15 +103,12 @@
   }
 }
 
-ScriptPromise SerialPortUnderlyingSource::ArmWatcher(
-    ScriptState* script_state) {
+void SerialPortUnderlyingSource::ArmWatcher() {
   MojoResult ready_result;
   mojo::HandleSignalsState ready_state;
   MojoResult result = watcher_.Arm(&ready_result, &ready_state);
-  if (result == MOJO_RESULT_OK) {
-    pending_pull_ = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
-    return pending_pull_->Promise();
-  }
+  if (result == MOJO_RESULT_OK)
+    return;
 
   DCHECK_EQ(ready_result, MOJO_RESULT_OK);
   if (ready_state.readable()) {
@@ -130,24 +117,15 @@
   } else if (ready_state.peer_closed()) {
     PipeClosed();
   }
-
-  return ScriptPromise::CastUndefined(script_state);
 }
 
 void SerialPortUnderlyingSource::OnHandleReady(
     MojoResult result,
     const mojo::HandleSignalsState& state) {
-  DCHECK(pending_pull_);
-
   switch (result) {
     case MOJO_RESULT_OK: {
       bool read_result = ReadData();
       DCHECK(read_result);
-      // If the pipe was closed |pending_pull_| will have been resolved.
-      if (pending_pull_) {
-        pending_pull_->Resolve();
-        pending_pull_ = nullptr;
-      }
       break;
     }
     case MOJO_RESULT_SHOULD_WAIT:
@@ -159,6 +137,17 @@
   }
 }
 
+void SerialPortUnderlyingSource::ExpectPipeClose() {
+  if (data_pipe_) {
+    // The pipe is still open. Wait for PipeClosed() to be called.
+    expect_close_ = true;
+    return;
+  }
+
+  Controller()->Close();
+  serial_port_->UnderlyingSourceClosed();
+}
+
 void SerialPortUnderlyingSource::PipeClosed() {
   if (pending_exception_) {
     Controller()->Error(pending_exception_);
@@ -174,10 +163,6 @@
 void SerialPortUnderlyingSource::Close() {
   watcher_.Cancel();
   data_pipe_.reset();
-  if (pending_pull_) {
-    pending_pull_->Resolve();
-    pending_pull_ = nullptr;
-  }
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/serial/serial_port_underlying_source.h b/third_party/blink/renderer/modules/serial/serial_port_underlying_source.h
index c4a803a..1d6116aa 100644
--- a/third_party/blink/renderer/modules/serial/serial_port_underlying_source.h
+++ b/third_party/blink/renderer/modules/serial/serial_port_underlying_source.h
@@ -12,7 +12,6 @@
 namespace blink {
 
 class DOMException;
-class ScriptPromiseResolver;
 class SerialPort;
 
 class SerialPortUnderlyingSource : public UnderlyingSourceBase {
@@ -28,7 +27,6 @@
 
   void SignalErrorImmediately(DOMException*);
   void SignalErrorOnClose(DOMException*);
-  void ExpectClose();
 
   void Trace(Visitor*) override;
 
@@ -37,15 +35,15 @@
   // |Controller()| or the pipe was closed, and false otherwise.
   bool ReadData();
 
-  ScriptPromise ArmWatcher(ScriptState*);
+  void ArmWatcher();
   void OnHandleReady(MojoResult, const mojo::HandleSignalsState&);
+  void ExpectPipeClose();
   void PipeClosed();
   void Close();
 
   mojo::ScopedDataPipeConsumerHandle data_pipe_;
   mojo::SimpleWatcher watcher_;
   Member<SerialPort> serial_port_;
-  Member<ScriptPromiseResolver> pending_pull_;
   Member<DOMException> pending_exception_;
   bool expect_close_ = false;
 };
diff --git a/third_party/blink/renderer/modules/xr/xr.cc b/third_party/blink/renderer/modules/xr/xr.cc
index 5cb9dbb4..4b69014c 100644
--- a/third_party/blink/renderer/modules/xr/xr.cc
+++ b/third_party/blink/renderer/modules/xr/xr.cc
@@ -466,17 +466,6 @@
   return frame_provider_;
 }
 
-bool XR::CanRequestNonImmersiveFrameData() const {
-  return !!magic_window_provider_;
-}
-
-void XR::GetNonImmersiveFrameData(
-    device::mojom::blink::XRFrameDataRequestOptionsPtr options,
-    device::mojom::blink::XRFrameDataProvider::GetFrameDataCallback callback) {
-  DCHECK(CanRequestNonImmersiveFrameData());
-  magic_window_provider_->GetFrameData(std::move(options), std::move(callback));
-}
-
 const device::mojom::blink::XREnvironmentIntegrationProviderAssociatedPtr&
 XR::xrEnvironmentProviderPtr() {
   return environment_provider_;
@@ -936,16 +925,18 @@
       std::move(session_ptr->display_info), session_ptr->uses_input_eventing,
       enabled_features);
 
+  frameProvider()->OnSessionStarted(session, std::move(session_ptr));
+
   if (query->mode() == XRSession::kModeImmersiveVR ||
       query->mode() == XRSession::kModeImmersiveAR) {
-    frameProvider()->BeginImmersiveSession(session, std::move(session_ptr));
     if (environment_integration) {
       // See Task Sources spreadsheet for more information:
       // https://docs.google.com/spreadsheets/d/1b-dus1Ug3A8y0lX0blkmOjJILisUASdj8x9YN_XMwYc/view
-      frameProvider()->GetDataProvider()->GetEnvironmentIntegrationProvider(
-          mojo::MakeRequest(&environment_provider_,
-                            GetExecutionContext()->GetTaskRunner(
-                                TaskType::kMiscPlatformAPI)));
+      frameProvider()
+          ->GetImmersiveDataProvider()
+          ->GetEnvironmentIntegrationProvider(mojo::MakeRequest(
+              &environment_provider_, GetExecutionContext()->GetTaskRunner(
+                                          TaskType::kMiscPlatformAPI)));
       environment_provider_.set_connection_error_handler(WTF::Bind(
           &XR::OnEnvironmentProviderDisconnect, WrapWeakPersistent(this)));
       LocalFrame* frame = GetFrame();
@@ -991,14 +982,9 @@
 
     if (query->mode() == XRSession::kModeImmersiveVR &&
         session->UsesInputEventing()) {
-      frameProvider()->GetDataProvider()->SetInputSourceButtonListener(
+      frameProvider()->GetImmersiveDataProvider()->SetInputSourceButtonListener(
           session->GetInputClickListener());
     }
-  } else {
-    magic_window_provider_.reset();
-    magic_window_provider_.Bind(std::move(session_ptr->data_provider));
-    magic_window_provider_.set_disconnect_handler(WTF::Bind(
-        &XR::OnMagicWindowProviderDisconnect, WrapWeakPersistent(this)));
   }
 
   UseCounter::Count(ExecutionContext::From(query->GetScriptState()),
@@ -1125,17 +1111,6 @@
   environment_provider_.reset();
 }
 
-// Ends all non-immersive sessions when the magic window provider got
-// disconnected.
-void XR::OnMagicWindowProviderDisconnect() {
-  for (auto& session : sessions_) {
-    if (!session->immersive() && !session->ended()) {
-      session->ForceEnd();
-    }
-  }
-  magic_window_provider_.reset();
-}
-
 void XR::Trace(blink::Visitor* visitor) {
   visitor->Trace(frame_provider_);
   visitor->Trace(sessions_);
diff --git a/third_party/blink/renderer/modules/xr/xr.h b/third_party/blink/renderer/modules/xr/xr.h
index 9d97333..f37f30d 100644
--- a/third_party/blink/renderer/modules/xr/xr.h
+++ b/third_party/blink/renderer/modules/xr/xr.h
@@ -59,11 +59,6 @@
 
   XRFrameProvider* frameProvider();
 
-  bool CanRequestNonImmersiveFrameData() const;
-  void GetNonImmersiveFrameData(
-      device::mojom::blink::XRFrameDataRequestOptionsPtr,
-      device::mojom::blink::XRFrameDataProvider::GetFrameDataCallback);
-
   const device::mojom::blink::XREnvironmentIntegrationProviderAssociatedPtr&
   xrEnvironmentProviderPtr();
 
@@ -283,7 +278,6 @@
   void Dispose();
 
   void OnEnvironmentProviderDisconnect();
-  void OnMagicWindowProviderDisconnect();
 
   // Indicates whether use of requestDevice has already been logged.
   bool did_log_supports_immersive_ = false;
@@ -302,10 +296,7 @@
 
   Member<XRFrameProvider> frame_provider_;
   HeapHashSet<WeakMember<XRSession>> sessions_;
-
   mojo::Remote<device::mojom::blink::VRService> service_;
-  mojo::Remote<device::mojom::blink::XRFrameDataProvider>
-      magic_window_provider_;
   device::mojom::blink::XREnvironmentIntegrationProviderAssociatedPtr
       environment_provider_;
   mojo::Receiver<device::mojom::blink::VRServiceClient> receiver_{this};
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
index c1015b9..6ef38e3 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.cc
@@ -66,33 +66,50 @@
   frame_transport_ = MakeGarbageCollected<XRFrameTransport>();
 }
 
-void XRFrameProvider::BeginImmersiveSession(
+void XRFrameProvider::OnSessionStarted(
     XRSession* session,
     device::mojom::blink::XRSessionPtr session_ptr) {
-  // Make sure the session is indeed an immersive one.
-  DCHECK(session && session->immersive());
+  DCHECK(session);
 
-  // Ensure we can only have one immersive session at a time.
-  DCHECK(!immersive_session_);
-  DCHECK(session_ptr->data_provider);
-  DCHECK(session_ptr->submit_frame_sink);
+  if (session->immersive()) {
+    // Ensure we can only have one immersive session at a time.
+    DCHECK(!immersive_session_);
+    DCHECK(session_ptr->data_provider);
+    DCHECK(session_ptr->submit_frame_sink);
 
-  immersive_session_ = session;
+    immersive_session_ = session;
 
-  immersive_data_provider_.Bind(std::move(session_ptr->data_provider));
-  immersive_data_provider_.set_disconnect_handler(WTF::Bind(
-      &XRFrameProvider::OnProviderConnectionError, WrapWeakPersistent(this)));
+    immersive_data_provider_.Bind(std::move(session_ptr->data_provider));
+    immersive_data_provider_.set_disconnect_handler(
+        WTF::Bind(&XRFrameProvider::OnProviderConnectionError,
+                  WrapWeakPersistent(this), WrapWeakPersistent(session)));
 
-  presentation_provider_.Bind(
-      std::move(session_ptr->submit_frame_sink->provider));
-  presentation_provider_.set_disconnect_handler(WTF::Bind(
-      &XRFrameProvider::OnProviderConnectionError, WrapWeakPersistent(this)));
+    immersive_presentation_provider_.Bind(
+        std::move(session_ptr->submit_frame_sink->provider));
+    immersive_presentation_provider_.set_disconnect_handler(
+        WTF::Bind(&XRFrameProvider::OnProviderConnectionError,
+                  WrapWeakPersistent(this), WrapWeakPersistent(session)));
 
-  frame_transport_->BindSubmitFrameClient(
-      std::move(session_ptr->submit_frame_sink->client_receiver));
-  frame_transport_->SetTransportOptions(
-      std::move(session_ptr->submit_frame_sink->transport_options));
-  frame_transport_->PresentChange();
+    frame_transport_->BindSubmitFrameClient(
+        std::move(session_ptr->submit_frame_sink->client_receiver));
+    frame_transport_->SetTransportOptions(
+        std::move(session_ptr->submit_frame_sink->transport_options));
+    frame_transport_->PresentChange();
+  } else {
+    // If a non-immersive session doesn't have a data provider, we don't
+    // need to store a reference to it.
+    if (!session_ptr->data_provider) {
+      return;
+    }
+
+    mojo::Remote<device::mojom::blink::XRFrameDataProvider> data_provider;
+    data_provider.Bind(std::move(session_ptr->data_provider));
+    data_provider.set_disconnect_handler(
+        WTF::Bind(&XRFrameProvider::OnProviderConnectionError,
+                  WrapWeakPersistent(this), WrapWeakPersistent(session)));
+
+    non_immersive_data_providers_.insert(session, std::move(data_provider));
+  }
 }
 
 void XRFrameProvider::OnFocusChanged() {
@@ -112,35 +129,41 @@
 
 // Ends the immersive session when the presentation or immersive data provider
 // got disconnected.
-void XRFrameProvider::OnProviderConnectionError() {
-  presentation_provider_.reset();
-  immersive_data_provider_.reset();
-  if (vsync_connection_failed_)
-    return;
-  immersive_session_->ForceEnd();
-  vsync_connection_failed_ = true;
+void XRFrameProvider::OnProviderConnectionError(XRSession* session) {
+  // This will call into |OnSessionEnded|, unless it has already ended.
+  session->ForceEnd();
 }
 
-// Called by the immersive session when it is ended.
-void XRFrameProvider::OnImmersiveSessionEnded() {
-  if (!immersive_session_)
-    return;
+void XRFrameProvider::OnSessionEnded(XRSession* session) {
+  if (session->immersive()) {
+    DCHECK(session == immersive_session_);
 
-  xr_->ExitPresent();
+    xr_->ExitPresent();
 
-  immersive_session_ = nullptr;
-  pending_immersive_vsync_ = false;
-  frame_id_ = -1;
-  presentation_provider_.reset();
-  immersive_data_provider_.reset();
+    immersive_session_ = nullptr;
+    pending_immersive_vsync_ = false;
+    frame_id_ = -1;
+    immersive_presentation_provider_.reset();
+    immersive_data_provider_.reset();
+    immersive_frame_pose_ = nullptr;
+    is_immersive_frame_position_emulated_ = false;
 
-  frame_transport_ = MakeGarbageCollected<XRFrameTransport>();
+    frame_transport_ = MakeGarbageCollected<XRFrameTransport>();
 
-  // When we no longer have an active immersive session schedule all the
-  // outstanding frames that were requested while the immersive session was
-  // active.
-  if (requesting_sessions_.size() > 0)
-    ScheduleNonImmersiveFrame(nullptr);
+    // When we no longer have an active immersive session schedule all the
+    // outstanding frames that were requested while the immersive session was
+    // active.
+    if (requesting_sessions_.size() > 0) {
+      for (auto& session : requesting_sessions_) {
+        RequestNonImmersiveFrameData(session.key.Get());
+      }
+
+      ScheduleNonImmersiveFrame(nullptr);
+    }
+  } else {
+    non_immersive_data_providers_.erase(session);
+    requesting_sessions_.erase(session);
+  }
 }
 
 // Schedule a session to be notified when the next XR frame is available.
@@ -159,13 +182,19 @@
 
   // Non-immersive frame logic.
 
-  requesting_sessions_.push_back(session);
+  // Duplicate frame requests are treated as a no-op.
+  if (requesting_sessions_.Contains(session)) {
+    return;
+  }
+  requesting_sessions_.insert(session, nullptr);
 
   // If there's an active immersive session save the request but suppress
   // processing it until the immersive session is no longer active.
-  if (immersive_session_)
+  if (immersive_session_) {
     return;
+  }
 
+  RequestNonImmersiveFrameData(session);
   ScheduleNonImmersiveFrame(std::move(options));
 }
 
@@ -201,23 +230,9 @@
   if (!doc)
     return;
 
-  // This is cleared by either OnNonImmersiveFrameData (GetFrameData callback,
-  // used if we have a magic window provider), or by OnNonImmersiveVSync
-  // (XRFrameProviderRequestCallback's invoke, used if there's no magic window
-  // provider).
   pending_non_immersive_vsync_ = true;
 
-  // If we have a Magic Window provider, request frame data and flag that
-  // we're waiting for it.  If not, clear any pose data, so that
-  // ProcessScheduledFrame handles it appropriately.
-  if (xr_->CanRequestNonImmersiveFrameData()) {
-    xr_->GetNonImmersiveFrameData(
-        std::move(options), WTF::Bind(&XRFrameProvider::OnNonImmersiveFrameData,
-                                      WrapWeakPersistent(this)));
-  } else {
-    frame_pose_ = nullptr;
-  }
-
+  // Calls |OnNonImmersiveVSync|
   doc->RequestAnimationFrame(
       MakeGarbageCollected<XRFrameProviderRequestCallback>(this));
 }
@@ -226,7 +241,6 @@
     device::mojom::blink::XRFrameDataPtr data) {
   TRACE_EVENT0("gpu", __FUNCTION__);
   DVLOG(2) << __FUNCTION__;
-  vsync_connection_failed_ = false;
   if (!data) {
     return;
   }
@@ -250,14 +264,17 @@
           .MonotonicTimeToZeroBasedDocumentTime(monotonic_time_now)
           .InMillisecondsF();
 
-  frame_pose_ = std::move(data->pose);
+  immersive_frame_pose_ = std::move(data->pose);
+  if (immersive_frame_pose_) {
+    is_immersive_frame_position_emulated_ =
+        immersive_frame_pose_->emulated_position;
+  } else {
+    is_immersive_frame_position_emulated_ = true;
+  }
+
   frame_id_ = data->frame_id;
   buffer_mailbox_holder_ = data->buffer_holder;
 
-  if (frame_pose_) {
-    emulated_position_ = frame_pose_->emulated_position;
-  }
-
   pending_immersive_vsync_ = false;
 
   // Post a task to handle scheduled animations after the current
@@ -295,13 +312,13 @@
 }
 
 void XRFrameProvider::OnNonImmersiveFrameData(
+    XRSession* session,
     device::mojom::blink::XRFrameDataPtr frame_data) {
   TRACE_EVENT0("gpu", __FUNCTION__);
   DVLOG(2) << __FUNCTION__;
 
   // TODO(https://crbug.com/837834): add unit tests for this code path.
 
-  pending_non_immersive_vsync_ = false;
   LocalFrame* frame = xr_->GetFrame();
   if (!frame)
     return;
@@ -309,17 +326,48 @@
   if (!doc)
     return;
 
-  if (!frame_data) {
-    // Unexpectedly didn't get frame data, and we don't have a timestamp.
-    // Try to request a regular animation frame to avoid getting stuck.
-    DVLOG(1) << __FUNCTION__ << ": NO FRAME DATA!";
-    frame_pose_ = nullptr;
-    doc->RequestAnimationFrame(
-        MakeGarbageCollected<XRFrameProviderRequestCallback>(this));
+  // Look up the request for this session. The session may have ended between
+  // when the request was sent and this callback, so skip it in that case.
+  auto request = requesting_sessions_.find(session);
+  if (request == requesting_sessions_.end()) {
     return;
   }
 
-  frame_pose_ = std::move(frame_data->pose);
+  if (frame_data) {
+    request->value = std::move(frame_data->pose);
+  } else {
+    // Unexpectedly didn't get frame data, and we don't have a timestamp.
+    // Try to request a regular animation frame to avoid getting stuck.
+    DVLOG(1) << __FUNCTION__ << ": NO FRAME DATA!";
+    request->value = nullptr;
+    doc->RequestAnimationFrame(
+        MakeGarbageCollected<XRFrameProviderRequestCallback>(this));
+  }
+}
+
+void XRFrameProvider::RequestNonImmersiveFrameData(XRSession* session) {
+  DCHECK(session);
+  DCHECK(!session->immersive());
+  DCHECK(!immersive_session_);
+
+  // The requesting_sessions_ entry for this session must have already
+  // been created in |RequestFrame|.
+  auto request = requesting_sessions_.find(session);
+  DCHECK(request != requesting_sessions_.end());
+
+  auto provider = non_immersive_data_providers_.find(session);
+  if (provider == non_immersive_data_providers_.end()) {
+    request->value = nullptr;
+  } else {
+    auto& data_provider = provider->value;
+    auto options = device::mojom::blink::XRFrameDataRequestOptions::New(
+        session->worldTrackingState()->planeDetectionState()->enabled());
+
+    data_provider->GetFrameData(
+        std::move(options),
+        WTF::Bind(&XRFrameProvider::OnNonImmersiveFrameData,
+                  WrapWeakPersistent(this), WrapWeakPersistent(session)));
+  }
 }
 
 void XRFrameProvider::ProcessScheduledFrame(
@@ -336,18 +384,24 @@
   }
 
   if (immersive_session_) {
+    // We need to ensure that pose data is valid for the duration of the frame,
+    // because input events may call into |session.end()| which will destroy
+    // this data otherwise. Move the data into local scope here so that it can't
+    // be destroyed.
+    auto frame_pose = std::move(immersive_frame_pose_);
+
     // Prior to updating input source state, update the state needed to create
     // presentation frame as newly created presentation frame will get passed to
     // the input source select[/start/end] events.
     immersive_session_->UpdatePresentationFrameState(
-        high_res_now_ms, getPoseMatrix(frame_pose_), frame_data,
-        emulated_position_);
+        high_res_now_ms, getPoseMatrix(frame_pose), frame_data,
+        is_immersive_frame_position_emulated_);
 
-    if (frame_pose_) {
+    if (frame_pose) {
       base::span<const device::mojom::blink::XRInputSourceStatePtr>
           input_states;
-      if (frame_pose_->input_state.has_value())
-        input_states = frame_pose_->input_state.value();
+      if (frame_pose->input_state.has_value())
+        input_states = frame_pose->input_state.value();
 
       immersive_session_->OnInputStateChange(frame_id_, input_states);
     }
@@ -357,7 +411,7 @@
     if (!immersive_session_)
       return;
 
-    if (frame_pose_ && frame_pose_->pose_reset) {
+    if (frame_pose && frame_pose->pose_reset) {
       immersive_session_->OnPoseReset();
     }
 
@@ -391,30 +445,32 @@
     // In the process of fulfilling the frame requests for each session they are
     // extremely likely to request another frame. Work off of a separate list
     // from the requests to prevent infinite loops.
-    DCHECK(processing_sessions_.IsEmpty());
-    swap(requesting_sessions_, processing_sessions_);
+    decltype(requesting_sessions_) processing_sessions;
+    swap(requesting_sessions_, processing_sessions);
 
     // Inform sessions with a pending request of the new frame
-    for (unsigned i = 0; i < processing_sessions_.size(); ++i) {
-      XRSession* session = processing_sessions_.at(i).Get();
+    for (auto& request : processing_sessions) {
+      XRSession* session = request.key.Get();
 
       // If the session was terminated between requesting and now, we shouldn't
       // process anything further.
       if (session->ended())
         continue;
 
+      const auto& frame_pose = request.value;
+
       // Prior to updating input source state, update the state needed to create
       // presentation frame as newly created presentation frame will get passed
       // to the input source select[/start/end] events.
-      session->UpdatePresentationFrameState(high_res_now_ms,
-                                            getPoseMatrix(frame_pose_),
-                                            frame_data, emulated_position_);
+      session->UpdatePresentationFrameState(
+          high_res_now_ms, getPoseMatrix(frame_pose), frame_data,
+          true /* Non-immersive positions are always emulated */);
 
-      if (frame_pose_) {
+      if (frame_pose) {
         base::span<const device::mojom::blink::XRInputSourceStatePtr>
             input_states;
-        if (frame_pose_->input_state.has_value())
-          input_states = frame_pose_->input_state.value();
+        if (frame_pose->input_state.has_value())
+          input_states = frame_pose->input_state.value();
 
         session->OnInputStateChange(frame_id_, input_states);
       }
@@ -424,7 +480,7 @@
       if (session->ended())
         continue;
 
-      if (frame_pose_ && frame_pose_->pose_reset) {
+      if (frame_pose && frame_pose->pose_reset) {
         session->OnPoseReset();
       }
 
@@ -434,8 +490,6 @@
 
       session->OnFrame(high_res_now_ms, base::nullopt);
     }
-
-    processing_sessions_.clear();
   }
 }
 
@@ -443,7 +497,7 @@
   DCHECK(layer);
   DCHECK(immersive_session_);
   DCHECK(layer->session() == immersive_session_);
-  if (!presentation_provider_.is_bound())
+  if (!immersive_presentation_provider_.is_bound())
     return;
 
   TRACE_EVENT1("gpu", "XRFrameProvider::SubmitWebGLLayer", "frame", frame_id_);
@@ -462,7 +516,7 @@
   if (!was_changed) {
     // Just tell the device side that there was no submitted frame instead of
     // executing the implicit end-of-frame submit.
-    frame_transport_->FrameSubmitMissing(presentation_provider_.get(),
+    frame_transport_->FrameSubmitMissing(immersive_presentation_provider_.get(),
                                          webgl_context->ContextGL(), frame_id_);
     return;
   }
@@ -478,9 +532,9 @@
     bool needs_copy = false;
     DVLOG(3) << __FUNCTION__ << ": FrameSubmit for SharedBuffer mode";
     frame_transport_->FrameSubmit(
-        presentation_provider_.get(), webgl_context->ContextGL(), webgl_context,
-        std::move(image_ref), std::move(image_release_callback), frame_id_,
-        needs_copy);
+        immersive_presentation_provider_.get(), webgl_context->ContextGL(),
+        webgl_context, std::move(image_ref), std::move(image_release_callback),
+        frame_id_, needs_copy);
     return;
   }
 
@@ -502,9 +556,9 @@
   bool needs_copy = immersive_session_->External();
 
   frame_transport_->FrameSubmit(
-      presentation_provider_.get(), webgl_context->ContextGL(), webgl_context,
-      std::move(image_ref), std::move(image_release_callback), frame_id_,
-      needs_copy);
+      immersive_presentation_provider_.get(), webgl_context->ContextGL(),
+      webgl_context, std::move(image_ref), std::move(image_release_callback),
+      frame_id_, needs_copy);
 
   // Reset our frame id, since anything we'd want to do (resizing/etc) can
   // no-longer happen to this frame.
@@ -515,7 +569,7 @@
 // the moment. Will need an overhaul when we get more robust layering support.
 void XRFrameProvider::UpdateWebGLLayerViewports(XRWebGLLayer* layer) {
   DCHECK(layer->session() == immersive_session_);
-  DCHECK(presentation_provider_);
+  DCHECK(immersive_presentation_provider_);
 
   XRViewport* left = layer->GetViewportForEye(XRView::kEyeLeft);
   XRViewport* right = layer->GetViewportForEye(XRView::kEyeRight);
@@ -541,12 +595,12 @@
                   static_cast<float>(right->height()) / height)
             : WebFloatRect();
 
-  presentation_provider_->UpdateLayerBounds(
+  immersive_presentation_provider_->UpdateLayerBounds(
       frame_id_, left_coords, right_coords, WebSize(width, height));
 }
 
 void XRFrameProvider::Dispose() {
-  presentation_provider_.reset();
+  immersive_presentation_provider_.reset();
   immersive_data_provider_.reset();
   // TODO(bajones): Do something for outstanding frame requests?
 }
@@ -555,8 +609,8 @@
   visitor->Trace(xr_);
   visitor->Trace(frame_transport_);
   visitor->Trace(immersive_session_);
+  visitor->Trace(non_immersive_data_providers_);
   visitor->Trace(requesting_sessions_);
-  visitor->Trace(processing_sessions_);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_frame_provider.h b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
index 2919508..8fd58cc 100644
--- a/third_party/blink/renderer/modules/xr/xr_frame_provider.h
+++ b/third_party/blink/renderer/modules/xr/xr_frame_provider.h
@@ -27,9 +27,9 @@
 
   XRSession* immersive_session() const { return immersive_session_; }
 
-  void BeginImmersiveSession(XRSession* session,
-                             device::mojom::blink::XRSessionPtr session_ptr);
-  void OnImmersiveSessionEnded();
+  void OnSessionStarted(XRSession* session,
+                        device::mojom::blink::XRSessionPtr session_ptr);
+  void OnSessionEnded(XRSession* session);
 
   void RequestFrame(XRSession*);
 
@@ -41,7 +41,7 @@
   void Dispose();
   void OnFocusChanged();
 
-  device::mojom::blink::XRFrameDataProvider* GetDataProvider() {
+  device::mojom::blink::XRFrameDataProvider* GetImmersiveDataProvider() {
     return immersive_data_provider_.get();
   }
 
@@ -49,32 +49,47 @@
 
  private:
   void OnImmersiveFrameData(device::mojom::blink::XRFrameDataPtr data);
-  void OnNonImmersiveFrameData(device::mojom::blink::XRFrameDataPtr data);
+  void OnNonImmersiveFrameData(XRSession* session,
+                               device::mojom::blink::XRFrameDataPtr data);
+
+  // Posts a request to the |XRFrameDataProvider| for the given session for
+  // frame data. If the given session has no provider, it will be given null
+  // frame data.
+  void RequestNonImmersiveFrameData(XRSession* session);
 
   // TODO(https://crbug.com/955819): options should be removed from those
   // methods as they'll no longer be passed on a per-frame basis.
   void ScheduleImmersiveFrame(
       device::mojom::blink::XRFrameDataRequestOptionsPtr options);
+
+  // Schedules an animation frame to service all non-immersive requesting
+  // sessions. This will be postponed if there is a currently running immmersive
+  // session.
   void ScheduleNonImmersiveFrame(
       device::mojom::blink::XRFrameDataRequestOptionsPtr options);
 
-  void OnProviderConnectionError();
+  void OnProviderConnectionError(XRSession* session);
   void ProcessScheduledFrame(device::mojom::blink::XRFrameDataPtr frame_data,
                              double high_res_now_ms);
 
   const Member<XR> xr_;
+
+  // Immersive session state
   Member<XRSession> immersive_session_;
   Member<XRFrameTransport> frame_transport_;
-
-  // Non-immersive Sessions which have requested a frame update.
-  HeapVector<Member<XRSession>> requesting_sessions_;
-  HeapVector<Member<XRSession>> processing_sessions_;
-
-  mojo::Remote<device::mojom::blink::XRPresentationProvider>
-      presentation_provider_;
   mojo::Remote<device::mojom::blink::XRFrameDataProvider>
       immersive_data_provider_;
-  device::mojom::blink::VRPosePtr frame_pose_;
+  mojo::Remote<device::mojom::blink::XRPresentationProvider>
+      immersive_presentation_provider_;
+  device::mojom::blink::VRPosePtr immersive_frame_pose_;
+  bool is_immersive_frame_position_emulated_ = false;
+
+  // Non-immersive session state
+  HeapHashMap<Member<XRSession>,
+              mojo::Remote<device::mojom::blink::XRFrameDataProvider>>
+      non_immersive_data_providers_;
+  HeapHashMap<Member<XRSession>, device::mojom::blink::VRPosePtr>
+      requesting_sessions_;
 
   // This frame ID is XR-specific and is used to track when frames arrive at the
   // XR compositor so that it knows which poses to use, when to apply bounds
@@ -82,12 +97,9 @@
   int16_t frame_id_ = -1;
   bool pending_immersive_vsync_ = false;
   bool pending_non_immersive_vsync_ = false;
-  bool vsync_connection_failed_ = false;
 
   base::Optional<gpu::MailboxHolder> buffer_mailbox_holder_;
   bool last_has_focus_ = false;
-
-  bool emulated_position_ = false;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/xr/xr_session.cc b/third_party/blink/renderer/modules/xr/xr_session.cc
index 2691f2d6..bdcad2b 100644
--- a/third_party/blink/renderer/modules/xr/xr_session.cc
+++ b/third_party/blink/renderer/modules/xr/xr_session.cc
@@ -802,11 +802,8 @@
     canvas_input_provider_ = nullptr;
   }
 
-  // If this session is the active immersive session, notify the frameProvider
-  // that it's ended.
-  if (xr_->frameProvider()->immersive_session() == this) {
-    xr_->frameProvider()->OnImmersiveSessionEnded();
-  }
+  // Notify the frame provider that we've ended
+  xr_->frameProvider()->OnSessionEnded(this);
 
   DispatchEvent(*XRSessionEvent::Create(event_type_names::kEnd, this));
 }
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
index 5f3a71d9..9889add4 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.cc
@@ -19,7 +19,6 @@
 #include "third_party/blink/renderer/platform/graphics/paint/paint_chunk_subset.h"
 #include "third_party/blink/renderer/platform/graphics/paint/raster_invalidation_tracking.h"
 #include "third_party/blink/renderer/platform/json/json_values.h"
-#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
 
 namespace blink {
 
@@ -149,8 +148,7 @@
 ContentLayerClientImpl::TakeDebugInfo(const cc::Layer* layer) {
   DCHECK_EQ(layer, cc_picture_layer_.get());
   auto traced_value = std::make_unique<base::trace_event::TracedValue>();
-  traced_value->SetString("layer_name",
-                          WTF::StringUTF8Adaptor(debug_name_).AsStringPiece());
+  traced_value->SetString("layer_name", LayerDebugName(layer));
   if (auto* tracking = raster_invalidator_.GetTracking()) {
     tracking->AddToTracedValue(*traced_value);
     tracking->ClearInvalidations();
@@ -160,6 +158,12 @@
   return traced_value;
 }
 
+std::string ContentLayerClientImpl::LayerDebugName(
+    const cc::Layer* layer) const {
+  DCHECK_EQ(layer, cc_picture_layer_.get());
+  return debug_name_.Utf8();
+}
+
 scoped_refptr<cc::PictureLayer> ContentLayerClientImpl::UpdateCcPictureLayer(
     scoped_refptr<const PaintArtifact> paint_artifact,
     const PaintChunkSubset& paint_chunks,
diff --git a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
index a75ad72..03d76178 100644
--- a/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
+++ b/third_party/blink/renderer/platform/graphics/compositing/content_layer_client_impl.h
@@ -47,6 +47,7 @@
   // cc::LayerClient
   std::unique_ptr<base::trace_event::TracedValue> TakeDebugInfo(
       const cc::Layer*) override;
+  std::string LayerDebugName(const cc::Layer*) const override;
   void DidChangeScrollbarsHiddenIfOverlay(bool) override {}
 
   bool Matches(const PaintChunk& paint_chunk) const {
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.cc b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
index b37753b..903bfa1 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.cc
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.cc
@@ -61,7 +61,6 @@
 #include "third_party/blink/renderer/platform/wtf/hash_map.h"
 #include "third_party/blink/renderer/platform/wtf/hash_set.h"
 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
-#include "third_party/blink/renderer/platform/wtf/text/string_utf8_adaptor.h"
 #include "third_party/blink/renderer/platform/wtf/text/text_stream.h"
 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
 
@@ -808,8 +807,7 @@
     const cc::Layer* layer) {
   auto traced_value = std::make_unique<base::trace_event::TracedValue>();
 
-  traced_value->SetString(
-      "layer_name", WTF::StringUTF8Adaptor(DebugName(layer)).AsStringPiece());
+  traced_value->SetString("layer_name", LayerDebugName(layer));
 
   traced_value->BeginArray("compositing_reasons");
   for (const char* description :
@@ -834,6 +832,10 @@
   return traced_value;
 }
 
+std::string GraphicsLayer::LayerDebugName(const cc::Layer* layer) const {
+  return DebugName(layer).Utf8();
+}
+
 void GraphicsLayer::DidChangeScrollbarsHiddenIfOverlay(bool hidden) {
   client_.SetOverlayScrollbarsHidden(hidden);
 }
diff --git a/third_party/blink/renderer/platform/graphics/graphics_layer.h b/third_party/blink/renderer/platform/graphics/graphics_layer.h
index 91e8f42..82162e5 100644
--- a/third_party/blink/renderer/platform/graphics/graphics_layer.h
+++ b/third_party/blink/renderer/platform/graphics/graphics_layer.h
@@ -236,6 +236,7 @@
   // cc::LayerClient implementation.
   std::unique_ptr<base::trace_event::TracedValue> TakeDebugInfo(
       const cc::Layer*) override;
+  std::string LayerDebugName(const cc::Layer*) const override;
   void DidChangeScrollbarsHiddenIfOverlay(bool) override;
 
   PaintController& GetPaintController() const;
diff --git a/third_party/blink/web_tests/animations/interpolation/viewport-unit-interpolation.html b/third_party/blink/web_tests/animations/interpolation/viewport-unit-interpolation.html
deleted file mode 100644
index 0a55445b..0000000
--- a/third_party/blink/web_tests/animations/interpolation/viewport-unit-interpolation.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!doctype html>
-<style>
-
-.target {
-    font-size: 16px;
-    width: 60px;
-    height: 60px;
-    display: inline-block;
-    border: 2px solid black;
-    margin-right: 2px;
-}
-.expected {
-    background-color: green;
-    margin-right: 15px;
-}
-
-</style>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-
-function vw(x) {
-    return (x * window.innerWidth / 100);
-}
-
-function calc(x) {
-    return Math.max(16 + (vw(10) - 16) * x, 0).toFixed(2) + "px";
-}
-
-assertInterpolation({
-    property: 'width',
-    from: '1em',
-    to: '10vw'
-}, [
-    {at: -0.3, is: calc(-0.3)},
-    {at: 0, is: calc(0)},
-    {at: 0.3, is: calc(0.3)},
-    {at: 0.6, is: calc(0.6)},
-    {at: 1, is: calc(1)},
-    {at: 1.5, is: calc(1.5)}
-]);
-</script>
-</body>
diff --git a/third_party/blink/web_tests/animations/interpolation/visibility-interpolation.html b/third_party/blink/web_tests/animations/interpolation/visibility-interpolation.html
deleted file mode 100644
index 7d366179..0000000
--- a/third_party/blink/web_tests/animations/interpolation/visibility-interpolation.html
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-<body>
-<script src="resources/interpolation-test.js"></script>
-<script>
-assertInterpolation({
-  property: 'visibility',
-  from: 'visible',
-  to: 'visible'
-}, [
-  {at: -1, is: 'visible'},
-  {at: 0, is: 'visible'},
-  {at: 0.5, is: 'visible'},
-  {at: 1, is: 'visible'},
-  {at: 1.5, is: 'visible'},
-]);
-
-assertInterpolation({
-  property: 'visibility',
-  from: 'visible',
-  to: 'hidden'
-}, [
-  {at: -1, is: 'visible'},
-  {at: 0, is: 'visible'},
-  {at: 0.1, is: 'visible'},
-  {at: 0.9, is: 'visible'},
-  {at: 1, is: 'hidden'},
-  {at: 1.5, is: 'hidden'},
-]);
-
-assertInterpolation({
-  property: 'visibility',
-  from: 'hidden',
-  to: 'visible'
-}, [
-  {at: -1, is: 'hidden'},
-  {at: 0, is: 'hidden'},
-  {at: 0.1, is: 'visible'},
-  {at: 0.9, is: 'visible'},
-  {at: 1, is: 'visible'},
-  {at: 1.5, is: 'visible'},
-]);
-
-assertInterpolation({
-  property: 'visibility',
-  from: 'collapse',
-  to: 'visible'
-}, [
-  {at: -1, is: 'collapse'},
-  {at: 0, is: 'collapse'},
-  {at: 0.1, is: 'visible'},
-  {at: 0.9, is: 'visible'},
-  {at: 1, is: 'visible'},
-  {at: 1.5, is: 'visible'},
-]);
-
-assertNoInterpolation({
-  property: 'visibility',
-  from: 'collapse',
-  to: 'hidden'
-});
-</script>
-</body>
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
index 756b84a..5f02d3e 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_6.json
@@ -54641,6 +54641,78 @@
      {}
     ]
    ],
+   "css/css-lists/list-style-type-string-002.html": [
+    [
+     "css/css-lists/list-style-type-string-002.html",
+     [
+      [
+       "/css/css-lists/list-style-type-string-002-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-lists/list-style-type-string-003.html": [
+    [
+     "css/css-lists/list-style-type-string-003.html",
+     [
+      [
+       "/css/css-lists/list-style-type-string-003-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-lists/list-style-type-string-004.html": [
+    [
+     "css/css-lists/list-style-type-string-004.html",
+     [
+      [
+       "/css/css-lists/list-style-type-string-004-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-lists/list-style-type-string-005a.html": [
+    [
+     "css/css-lists/list-style-type-string-005a.html",
+     [
+      [
+       "/css/css-lists/list-style-type-string-005-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-lists/list-style-type-string-005b.html": [
+    [
+     "css/css-lists/list-style-type-string-005b.html",
+     [
+      [
+       "/css/css-lists/list-style-type-string-005-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
+   "css/css-lists/list-style-type-string-006.html": [
+    [
+     "css/css-lists/list-style-type-string-006.html",
+     [
+      [
+       "/css/css-lists/list-style-type-string-006-ref.html",
+       "=="
+      ]
+     ],
+     {}
+    ]
+   ],
    "css/css-lists/list-type-none-style-image.html": [
     [
      "css/css-lists/list-type-none-style-image.html",
@@ -124957,9 +125029,6 @@
    "common/echo.py": [
     []
    ],
-   "common/form-submission.py": [
-    []
-   ],
    "common/get-host-info.sub.js": [
     []
    ],
@@ -139021,6 +139090,21 @@
    "css/css-lists/list-marker-with-lineheight-and-overflow-hidden-001-ref.html": [
     []
    ],
+   "css/css-lists/list-style-type-string-002-ref.html": [
+    []
+   ],
+   "css/css-lists/list-style-type-string-003-ref.html": [
+    []
+   ],
+   "css/css-lists/list-style-type-string-004-ref.html": [
+    []
+   ],
+   "css/css-lists/list-style-type-string-005-ref.html": [
+    []
+   ],
+   "css/css-lists/list-style-type-string-006-ref.html": [
+    []
+   ],
    "css/css-lists/list-type-none-style-image-ref.html": [
     []
    ],
@@ -160093,6 +160177,9 @@
    "html/semantics/forms/form-submission-0/resources/file-submission.py": [
     []
    ],
+   "html/semantics/forms/form-submission-0/resources/form-submission.py": [
+    []
+   ],
    "html/semantics/forms/form-submission-0/resources/targetted-form.js": [
     []
    ],
@@ -338617,10 +338704,6 @@
    "2ee403645b1bb27cdb27bcea716e166729b81af8",
    "support"
   ],
-  "common/form-submission.py": [
-   "467875453c9dc64aac51add3f4a617d941820972",
-   "support"
-  ],
   "common/get-host-info.sub.js": [
    "1fbf2dbc68791f3188b0b82c71198bc409fb007d",
    "support"
@@ -383821,6 +383904,50 @@
    "4852e423273efe6ee99a832b141f943e7a3c4bca",
    "visual"
   ],
+  "css/css-lists/list-style-type-string-002-ref.html": [
+   "8694b5f54424c9a6e96209950a5585352a836403",
+   "support"
+  ],
+  "css/css-lists/list-style-type-string-002.html": [
+   "6f00d8ac4fc8a986085e860d6ff0c0ba6aa8c4aa",
+   "reftest"
+  ],
+  "css/css-lists/list-style-type-string-003-ref.html": [
+   "ea3601b9598255ddb57e6d17d273d32bdad73315",
+   "support"
+  ],
+  "css/css-lists/list-style-type-string-003.html": [
+   "94467ca1f21c8807a57b3ecd10a6ba0d86ae5049",
+   "reftest"
+  ],
+  "css/css-lists/list-style-type-string-004-ref.html": [
+   "3a828b28893ba3d8627a8c194f6ec13ebcc17c70",
+   "support"
+  ],
+  "css/css-lists/list-style-type-string-004.html": [
+   "922a4ecea8605cb82f2804832c13112ce0071fc2",
+   "reftest"
+  ],
+  "css/css-lists/list-style-type-string-005-ref.html": [
+   "703ceff068ec547137b107472dd7ce912e6abe2a",
+   "support"
+  ],
+  "css/css-lists/list-style-type-string-005a.html": [
+   "725cdee1a83555dbb891a5815e7f69f7430428c3",
+   "reftest"
+  ],
+  "css/css-lists/list-style-type-string-005b.html": [
+   "ca984b6d3be4a75e0b192da6bb9138cb1c600137",
+   "reftest"
+  ],
+  "css/css-lists/list-style-type-string-006-ref.html": [
+   "5c0a51329e5860fb5f06a7f3d75aeddd08284a9a",
+   "support"
+  ],
+  "css/css-lists/list-style-type-string-006.html": [
+   "7f6904b777f9a76fa405c36b58148bfb521523a1",
+   "reftest"
+  ],
   "css/css-lists/list-type-none-style-image-ref.html": [
    "9e72fb310233145c5a6c014c3febb2975586d76d",
    "support"
@@ -383930,7 +384057,7 @@
    "testharness"
   ],
   "css/css-lists/parsing/list-style-type-computed-expected.txt": [
-   "eabf33a193d5bbb194bcaa708f13b2f2e474d216",
+   "2ee6f4c99afb2b258f06fd4e9b54d0bad13f0718",
    "support"
   ],
   "css/css-lists/parsing/list-style-type-computed.html": [
@@ -383942,7 +384069,7 @@
    "testharness"
   ],
   "css/css-lists/parsing/list-style-type-valid-expected.txt": [
-   "b19f911c7831381295946c94ee797db03cca79c2",
+   "1687a520a08aa5cf2bcfbfdd46feff4b42814e47",
    "support"
   ],
   "css/css-lists/parsing/list-style-type-valid.html": [
@@ -407922,7 +408049,7 @@
    "testharness"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/list-style-type-expected.txt": [
-   "af42e021533c70e9607355fe7885205c0312c2e2",
+   "59617c5d72914839cb9b0d5200a38219f64167bc",
    "support"
   ],
   "css/css-typed-om/the-stylepropertymap/properties/list-style-type.html": [
@@ -419214,11 +419341,11 @@
    "testharness"
   ],
   "css/cssom-view/idlharness-expected.txt": [
-   "3193caa0c70724c96a720930bc8fd1f0e31af5ed",
+   "2d48e182979a0967befa2263fb3548c8df13ad8d",
    "support"
   ],
   "css/cssom-view/idlharness.html": [
-   "d3de0cfa595381e0b7c0f3188ac9322ef10608f5",
+   "d408c5cdb534b365d3a7dbbab1c71d2c1b7b1daa",
    "testharness"
   ],
   "css/cssom-view/iframe.html": [
@@ -453877,6 +454004,10 @@
    "5fc67faa880ffa9300a093aa0ef1f67c3a76eb0c",
    "support"
   ],
+  "html/semantics/forms/form-submission-0/resources/form-submission.py": [
+   "467875453c9dc64aac51add3f4a617d941820972",
+   "support"
+  ],
   "html/semantics/forms/form-submission-0/resources/targetted-form.js": [
    "6b6685291d2bd3d7dc64f97e2d9e460394d2eb3c",
    "support"
@@ -453886,7 +454017,7 @@
    "testharness"
   ],
   "html/semantics/forms/form-submission-0/submit-entity-body.html": [
-   "8363f39ff32cd165a28086b3c594b55a79ced8e5",
+   "f6f3858d4ff025cfbb2ba6b0745df51c6d39436b",
    "testharness"
   ],
   "html/semantics/forms/form-submission-0/submit-file.sub.html": [
@@ -490858,7 +490989,7 @@
    "support"
   ],
   "resources/idlharness.js": [
-   "ce5a8d86d2ebb4cc3ce33f5e6e7edc10d8bef33a",
+   "7d1373ef34110dd51c7e3be5181ede6ba5e3cee1",
    "support"
   ],
   "resources/idlharness.js.headers": [
diff --git a/third_party/blink/web_tests/storage/indexeddb/blob-contenttype.html b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-contenttype.any.js
similarity index 61%
rename from third_party/blink/web_tests/storage/indexeddb/blob-contenttype.html
rename to third_party/blink/web_tests/external/wpt/IndexedDB/blob-contenttype.any.js
index fec34e07..6faa0ad4 100644
--- a/third_party/blink/web_tests/storage/indexeddb/blob-contenttype.html
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-contenttype.any.js
@@ -1,24 +1,5 @@
-<!DOCTYPE html>
-<script src="../../resources/testharness.js"></script>
-<script src="../../resources/testharnessreport.js"></script>
-<script>
-
-function indexeddb_test(upgrade_func, body_func, description) {
-    async_test(function(t) {
-        var dbname = location.pathname + ' - ' + description;
-        var deleteRequest = indexedDB.deleteDatabase(dbname);
-        deleteRequest.onsuccess = t.step_func(function() {
-            var openRequest = indexedDB.open(dbname);
-            openRequest.onupgradeneeded = t.step_func(function() {
-                upgrade_func(t, openRequest.result);
-            });
-            openRequest.onsuccess = t.step_func(function() {
-                body_func(t, openRequest.result);
-            });
-            openRequest.onerror = t.unreached_func('open failed');
-        });
-    }, description);
-}
+// META: title=Blob Content Type
+// META: script=support.js
 
 indexeddb_test(
     function upgrade(t, db) {
@@ -54,6 +35,4 @@
         });
     },
     'Ensure that content type round trips when reading blob data'
-);
-
-</script>
+);
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/blob-delete-objectstore-db.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-delete-objectstore-db.any.js
new file mode 100644
index 0000000..61d7bad9
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-delete-objectstore-db.any.js
@@ -0,0 +1,47 @@
+// META: title=Blob Delete Object Store
+// META: script=support.js
+
+let key = "blob key";
+
+indexeddb_test(
+    function upgrade(t, db) {
+      const store0 = db.createObjectStore('store0');
+      const store1 = db.createObjectStore('store1');
+
+      const blobAContent = "First blob content";
+      const blobA = new Blob([blobAContent], {"type" : "text/plain"});
+
+      store0.put(blobA, key);
+    },
+    function success(t, db) {
+      db.close();
+      const request = indexedDB.open(db.name, 2);
+
+      request.onupgradeneeded = t.step_func(function(e) {
+        const db = e.target.result;
+        db.deleteObjectStore('store0');
+
+        request.onsuccess = t.step_func(function() {
+          const blobBContent = "Second blob content";
+          const trans = db.transaction('store1', 'readwrite');
+          const store1 = trans.objectStore('store1');
+          const blobB = new Blob([blobBContent], {"type" : "text/plain"});
+          store1.put(blobB, key);
+
+          trans.oncomplete = t.step_func(function() {
+            db.close();
+            const delete_request = indexedDB.deleteDatabase(db.name);
+
+            // The test passes if it successfully completes.
+            delete_request.onsuccess = t.step_func_done();
+
+            delete_request.onerror = t.unreached_func("Request should not fail.");
+          });
+
+          trans.onabort = t.unreached_func("Transaction should not be aborted.");
+        });
+      });
+      request.onsuccess = t.unreached_func("Request should not succeed without an upgrade.");
+      request.onerror = t.unreached_func("Request should not fail.");
+      request.onblocked = t.unreached_func("Request should not be blocked.");
+    }, "Deleting an object store and a database containing blobs doesn't crash.");
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/blob-valid-after-deletion.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-valid-after-deletion.any.js
new file mode 100644
index 0000000..7c7825c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-valid-after-deletion.any.js
@@ -0,0 +1,53 @@
+// META: title=Blob Valid After Deletion
+// META: script=support.js
+
+let key = "key";
+
+indexeddb_test(
+  function upgrade(t, db) {
+    db.createObjectStore('store');
+  },
+  function success(t, db) {
+    const blobAContent = "Blob A content";
+    const blobBContent = "Blob B content";
+    const blobA = new Blob([blobAContent], {"type" : "text/plain"});
+    const blobB = new Blob([blobBContent], {"type" : "text/plain"});
+    value = { a0: blobA, a1: blobA, b0: blobB };
+
+    const tx = db.transaction('store', 'readwrite');
+    var store = tx.objectStore('store');
+
+    store.put(value, key);
+    value = null;
+
+    const trans = db.transaction('store');
+    store = trans.objectStore('store');
+    const request = store.get(key);
+
+    request.onsuccess = t.step_func(function() {
+      const record = request.result;
+
+      trans.oncomplete = t.step_func(function() {
+        const trans = db.transaction('store', 'readwrite');
+        store = trans.objectStore('store');
+        const request = store.delete(key);
+
+        trans.oncomplete = t.step_func(function() {
+          const promise1 = record.a0.text().then(t.step_func(text => { assert_equals(text, blobAContent); },
+            t.unreached_func()));
+
+          const promise2 = record.a1.text().then(t.step_func(text => { assert_equals(text, blobAContent); },
+            t.unreached_func()));
+
+          const promise3 = record.b0.text().then(t.step_func(text => { assert_equals(text, blobBContent); },
+            t.unreached_func()));
+
+          Promise.all([promise1, promise2, promise3]).then(function() {
+            // The test passes if it successfully completes.
+            t.done();
+          });
+        });
+      });
+    });
+  },
+  "Blobs stay alive after their records are deleted.");
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/IndexedDB/blob-valid-before-commit.any.js b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-valid-before-commit.any.js
new file mode 100644
index 0000000..0803c9e
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/IndexedDB/blob-valid-before-commit.any.js
@@ -0,0 +1,41 @@
+// META: title=Blob Valid Before Commit
+// META: script=support.js
+
+let key = "key";
+
+indexeddb_test(
+    function upgrade(t, db) {
+        db.createObjectStore('store');
+    },
+    function success(t, db) {
+      const blobAContent = "Blob A content";
+      const blobBContent = "Blob B content";
+      const blobA = new Blob([blobAContent], {"type" : "text/plain"});
+      const blobB = new Blob([blobBContent], {"type" : "text/plain"});
+      const value = { a0: blobA, a1: blobA, b0: blobB };
+
+      const tx = db.transaction('store', 'readwrite');
+      const store = tx.objectStore('store');
+
+      store.put(value, key);
+      const request = store.get(key);
+
+      request.onsuccess = t.step_func(function() {
+        const record = request.result;
+
+        const promise1 = record.a0.text().then(t.step_func(text => { assert_equals(text, blobAContent); },
+            t.unreached_func()));
+
+        const promise2 = record.a1.text().then(t.step_func(text => { assert_equals(text, blobAContent); },
+            t.unreached_func()));
+
+        const promise3 = record.b0.text().then(t.step_func(text => { assert_equals(text, blobBContent); },
+            t.unreached_func()));
+
+        Promise.all([promise1, promise2, promise3]).then(function() {
+          // The test passes if it successfully completes.
+          t.done();
+        });
+      });
+    },
+    "Blobs can be read back before their records are committed.");
\ No newline at end of file
diff --git a/third_party/blink/web_tests/external/wpt/css/CSS2/visufx/animation/visibility-interpolation.html b/third_party/blink/web_tests/external/wpt/css/CSS2/visufx/animation/visibility-interpolation.html
new file mode 100644
index 0000000..683b393a
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/css/CSS2/visufx/animation/visibility-interpolation.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<meta charset="UTF-8">
+<title>visibility interpolation</title>
+<link rel="help" href="https://www.w3.org/TR/CSS2/visufx.html#visibility">
+<meta name="assert" content="visibility supports">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/interpolation-testcommon.js"></script>
+
+<body>
+<script>
+test_interpolation({
+  property: 'visibility',
+  from: 'visible',
+  to: 'visible'
+}, [
+  {at: -1, expect: 'visible'},
+  {at: 0, expect: 'visible'},
+  {at: 0.5, expect: 'visible'},
+  {at: 1, expect: 'visible'},
+  {at: 1.5, expect: 'visible'},
+]);
+
+test_interpolation({
+  property: 'visibility',
+  from: 'visible',
+  to: 'hidden'
+}, [
+  {at: -1, expect: 'visible'},
+  {at: 0, expect: 'visible'},
+  {at: 0.1, expect: 'visible'},
+  {at: 0.9, expect: 'visible'},
+  {at: 1, expect: 'hidden'},
+  {at: 1.5, expect: 'hidden'},
+]);
+
+test_interpolation({
+  property: 'visibility',
+  from: 'hidden',
+  to: 'visible'
+}, [
+  {at: -1, expect: 'hidden'},
+  {at: 0, expect: 'hidden'},
+  {at: 0.1, expect: 'visible'},
+  {at: 0.9, expect: 'visible'},
+  {at: 1, expect: 'visible'},
+  {at: 1.5, expect: 'visible'},
+]);
+
+test_interpolation({
+  property: 'visibility',
+  from: 'collapse',
+  to: 'visible'
+}, [
+  {at: -1, expect: 'collapse'},
+  {at: 0, expect: 'collapse'},
+  {at: 0.1, expect: 'visible'},
+  {at: 0.9, expect: 'visible'},
+  {at: 1, expect: 'visible'},
+  {at: 1.5, expect: 'visible'},
+]);
+
+test_no_interpolation({
+  property: 'visibility',
+  from: 'collapse',
+  to: 'hidden'
+});
+</script>
+</body>
diff --git a/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/width-interpolation.html b/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/width-interpolation.html
index 16b67c5..d165c99 100644
--- a/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/width-interpolation.html
+++ b/third_party/blink/web_tests/external/wpt/css/css-sizing/animation/width-interpolation.html
@@ -14,6 +14,7 @@
   overflow: visible;
 }
 .target {
+  font-size: 16px;
   background-color: black;
   width: 10px;
   height: 10px;
@@ -95,5 +96,32 @@
   {at: 1, expect: '100px'},
   {at: 1.5, expect: '145px'}
 ]);
+
+// The "vw" unit equals to 1% of the width of the viewport's initial containing
+// block:
+// https://developer.mozilla.org/en-US/docs/Web/CSS/length
+function vw(x) {
+    return (x * window.innerWidth / 100);
+}
+
+// In here, 16 is the font-size which is the value of 1em, and vw(10) is the
+// value of 10vw. The calc here takes the "at" in the next interpolation test
+// and computes the expected value.
+function calc(x) {
+    return Math.max(16 + (vw(10) - 16) * x, 0).toFixed(2) + "px";
+}
+
+test_interpolation({
+    property: 'width',
+    from: '1em',
+    to: '10vw'
+}, [
+    {at: -0.3, expect: calc(-0.3)},
+    {at: 0, expect: calc(0)},
+    {at: 0.3, expect: calc(0.3)},
+    {at: 0.6, expect: calc(0.6)},
+    {at: 1, expect: calc(1)},
+    {at: 1.5, expect: calc(1.5)}
+]);
 </script>
 </body>
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt b/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt
index 3193caa0..2d48e18 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness-expected.txt
@@ -1,5 +1,5 @@
 This is a testharness.js-based test.
-Found 359 tests; 284 PASS, 75 FAIL, 0 TIMEOUT, 0 NOTRUN.
+Found 379 tests; 304 PASS, 75 FAIL, 0 TIMEOUT, 0 NOTRUN.
 PASS idl_test setup
 PASS idl_test validation
 PASS Partial interface Window: original interface defined
@@ -123,6 +123,26 @@
 FAIL CSSPseudoElement interface: operation convertQuadFromNode(DOMQuadInit, GeometryNode, ConvertCoordinateOptions) assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
 FAIL CSSPseudoElement interface: operation convertRectFromNode(DOMRectReadOnly, GeometryNode, ConvertCoordinateOptions) assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
 FAIL CSSPseudoElement interface: operation convertPointFromNode(DOMPointInit, GeometryNode, ConvertCoordinateOptions) assert_own_property: self does not have own property "CSSPseudoElement" expected property "CSSPseudoElement" missing
+PASS MouseEvent interface: attribute screenX
+PASS MouseEvent interface: attribute screenY
+PASS MouseEvent interface: attribute pageX
+PASS MouseEvent interface: attribute pageY
+PASS MouseEvent interface: attribute clientX
+PASS MouseEvent interface: attribute clientY
+PASS MouseEvent interface: attribute x
+PASS MouseEvent interface: attribute y
+PASS MouseEvent interface: attribute offsetX
+PASS MouseEvent interface: attribute offsetY
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "screenX" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "screenY" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "pageX" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "pageY" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "clientX" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "clientY" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "x" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "y" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "offsetX" with the proper type
+PASS MouseEvent interface: new MouseEvent("foo") must inherit property "offsetY" with the proper type
 PASS HTMLElement interface: attribute offsetParent
 PASS HTMLElement interface: attribute offsetTop
 PASS HTMLElement interface: attribute offsetLeft
diff --git a/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness.html b/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness.html
index d3de0cfa..d408c5c 100644
--- a/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness.html
+++ b/third_party/blink/web_tests/external/wpt/css/cssom-view/idlharness.html
@@ -34,7 +34,7 @@
       HTMLElement: ['document.createElement("div")'],
       HTMLImageElement: ['document.createElement("img")'],
       Range: ['new Range()'],
-      // MouseEvent: ['new MouseEvent("foo")'],
+      MouseEvent: ['new MouseEvent("foo")'],
       Text: ['document.createTextNode("x")'],
       // CSSPseudoElement: [],
     });
diff --git a/third_party/blink/web_tests/external/wpt/common/form-submission.py b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/resources/form-submission.py
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/common/form-submission.py
rename to third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/resources/form-submission.py
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/submit-entity-body.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/submit-entity-body.html
index 8363f39..f6f3858 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/submit-entity-body.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/form-submission-0/submit-entity-body.html
@@ -96,7 +96,7 @@
   var testframe = document.getElementById("testframe");
   var testdocument = testframe.contentWindow.document;
   testdocument.body.innerHTML =
-    "<form id=testform method=post action=\"form-submission.py\" enctype=\"" + test_obj.enctype + "\">" +
+    "<form id=testform method=post action=\"/html/semantics/forms/form-submission-0/resources/form-submission.py\" enctype=\"" + test_obj.enctype + "\">" +
     test_obj.input +
     test_obj.submitelement +
     "</form>";
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/range-setattribute-value-ref.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/range-setattribute-value-ref.html
new file mode 100644
index 0000000..71a44cd
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/range-setattribute-value-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>range input element setAttribute value appearance</title>
+
+<p>Test passes if the range element below visually has its slider at 2/10 from the left</p>
+
+<input type=range min=0 max=10 value=2></input>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/range-setattribute-value.html b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/range-setattribute-value.html
new file mode 100644
index 0000000..3a03a5b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/forms/the-input-element/range-setattribute-value.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<link rel="author" title="Joey Arhar" href="mailto:jarhar@chromium.org">
+<link rel="help" href="https://html.spec.whatwg.org/multipage/input.html#the-input-element">
+<link rel="match" href="range-setattribute-value-ref.html">
+<title>range input element setAttribute value appearance</title>
+
+<p>Test passes if the range element below visually has its slider at 2/10 from the left</p>
+
+<script>
+window.onload = () => {
+
+  const input = document.createElement('input');
+  input.type = 'range';
+  input.min = 0;
+  input.max = 10;
+  document.body.appendChild(input);
+
+  requestAnimationFrame(() => {
+    requestAnimationFrame(() => {
+      input.setAttribute('value', 2);
+      document.documentElement.classList.remove('reftest-wait');
+    });
+  });
+};
+</script>
diff --git a/third_party/blink/web_tests/external/wpt/portals/portals-api.html b/third_party/blink/web_tests/external/wpt/portals/portals-api.html
new file mode 100644
index 0000000..79d2d52
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/portals/portals-api.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<title>Portals API test</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+  <script>
+    test(function() {
+      assert_true(document.createElement('portal') instanceof HTMLPortalElement);
+    }, "portal element exists");
+  </script>
+</body>
diff --git a/third_party/blink/web_tests/portals/portals-no-frame-crash.html b/third_party/blink/web_tests/external/wpt/portals/portals-no-frame-crash.html
similarity index 67%
rename from third_party/blink/web_tests/portals/portals-no-frame-crash.html
rename to third_party/blink/web_tests/external/wpt/portals/portals-no-frame-crash.html
index 34ad747..c87afa3 100644
--- a/third_party/blink/web_tests/portals/portals-no-frame-crash.html
+++ b/third_party/blink/web_tests/external/wpt/portals/portals-no-frame-crash.html
@@ -1,18 +1,18 @@
 <!DOCTYPE html>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
 <body>
 <script>
-  test(t => {
+  test(() => {
     let portal = document.createElement("portal");
-    xmlDoc = document.implementation.createDocument("", null);
+    let xmlDoc = document.implementation.createDocument("", null);
     xmlDoc.appendChild(portal);
   }, "inserting a portal element into an XML document shouldn't crash or throw");
 
-  test(t => {
+  test(() => {
     let iframe = document.createElement("iframe");
     document.body.appendChild(iframe);
-    var doc = iframe.contentDocument;
+    let doc = iframe.contentDocument;
     iframe.remove();
     let portal = document.createElement("portal");
     doc.body.appendChild(portal);
diff --git a/third_party/blink/web_tests/external/wpt/resources/idlharness.js b/third_party/blink/web_tests/external/wpt/resources/idlharness.js
index ce5a8d8..7d1373e 100644
--- a/third_party/blink/web_tests/external/wpt/resources/idlharness.js
+++ b/third_party/blink/web_tests/external/wpt/resources/idlharness.js
@@ -811,14 +811,15 @@
 
             if (this.members[rhs].members.length) {
                 test(function () {
+                    var clash = this.members[rhs].members.find(function(member) {
+                        return this.members[lhs].members.find(function(m) {
+                            return this.are_duplicate_members(m, member);
+                        }.bind(this));
+                    }.bind(this));
                     this.members[rhs].members.forEach(function(member) {
-                        assert_false(
-                            this.members[lhs].members.some(function (m) {
-                                return m.name === member.name
-                            }),
-                            "member " + member.name  + " is already defined");
                         this.members[lhs].members.push(new IdlInterfaceMember(member));
                     }.bind(this));
+                    assert_true(!clash, "member " + (clash && clash.name) + " is unique");
                 }.bind(this), lhs + " implements " + rhs + ": member names are unique");
             }
         }.bind(this));
@@ -837,12 +838,18 @@
 
             if (this.members[rhs].members.length) {
                 test(function () {
+                    var clash = this.members[rhs].members.find(function(member) {
+                        return this.members[lhs].members.find(function(m) {
+                            return this.are_duplicate_members(m, member);
+                        }.bind(this));
+                    }.bind(this));
                     this.members[rhs].members.forEach(function(member) {
                         assert_true(
                             this.members[lhs].members.every(m => !this.are_duplicate_members(m, member)),
                             "member " + member.name + " is unique");
                         this.members[lhs].members.push(new IdlInterfaceMember(member));
                     }.bind(this));
+                    assert_true(!clash, "member " + (clash && clash.name) + " is unique");
                 }.bind(this), lhs + " includes " + rhs + ": member names are unique");
             }
         }.bind(this));
@@ -974,13 +981,16 @@
         }
         if (parsed_idl.members.length) {
             test(function () {
+                var clash = parsed_idl.members.find(function(member) {
+                    return this.members[parsed_idl.name].members.find(function(m) {
+                        return this.are_duplicate_members(m, member);
+                    }.bind(this));
+                }.bind(this));
                 parsed_idl.members.forEach(function(member)
                 {
-                    assert_true(
-                        this.members[parsed_idl.name].members.every(m => !this.are_duplicate_members(m, member)),
-                        "member " + member.name + " is unique");
                     this.members[parsed_idl.name].members.push(new IdlInterfaceMember(member));
                 }.bind(this));
+                assert_true(!clash, "member " + (clash && clash.name) + " is unique");
             }.bind(this), `Partial ${parsed_idl.type} ${partialTestName}: member names are unique`);
         }
     }.bind(this));
diff --git a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
index 168b7de..b7b08bb 100644
--- a/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
+++ b/third_party/blink/web_tests/external/wpt/tools/wptrunner/wptrunner/browsers/android_webview.py
@@ -30,6 +30,7 @@
 
 def browser_kwargs(test_type, run_info_data, config, **kwargs):
     return {"binary": kwargs["binary"],
+            "device_serial": kwargs["device_serial"],
             "webdriver_binary": kwargs["webdriver_binary"],
             "webdriver_args": kwargs.get("webdriver_args")}
 
@@ -78,19 +79,25 @@
     """
 
     def __init__(self, logger, binary, webdriver_binary="chromedriver",
+                 device_serial=None,
                  webdriver_args=None):
         """Creates a new representation of Chrome.  The `binary` argument gives
         the browser binary to use for testing."""
         Browser.__init__(self, logger)
         self.binary = binary
+        self.device_serial = device_serial
         self.server = ChromeDriverServer(self.logger,
                                          binary=webdriver_binary,
                                          args=webdriver_args)
         self.setup_adb_reverse()
 
     def _adb_run(self, args):
-        self.logger.info('adb ' + ' '.join(args))
-        subprocess.check_call(['adb'] + args)
+        cmd = ['adb']
+        if self.device_serial:
+            cmd.extend(['-s', self.device_serial])
+        cmd.extend(args)
+        self.logger.info(' '.join(cmd))
+        subprocess.check_call(cmd)
 
     def setup_adb_reverse(self):
         self._adb_run(['wait-for-device'])
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/global-listeners-sidebar-a11y-test-expected.txt b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/global-listeners-sidebar-a11y-test-expected.txt
new file mode 100644
index 0000000..ff53d95c3
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/global-listeners-sidebar-a11y-test-expected.txt
@@ -0,0 +1,14 @@
+Adding global listener.
+Dumping event listeners view:
+
+======== touchstart ========
+== Raw
+[expanded] WindowRemoveToggle Passiveglobal-listeners-sidebar-a11y-test.js:13
+    handler: () => console.log
+    once: false
+    passive: true
+    useCapture: false
+Running the axe-core linter on the global listeners pane.
+aXe violations: []
+
+
diff --git a/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/global-listeners-sidebar-a11y-test.js b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/global-listeners-sidebar-a11y-test.js
new file mode 100644
index 0000000..4069fa02
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/devtools/a11y-axe-core/sources/global-listeners-sidebar-a11y-test.js
@@ -0,0 +1,23 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+(async function() {
+  await TestRunner.loadModule('axe_core_test_runner');
+  await TestRunner.loadModule('elements_test_runner');
+  await TestRunner.loadModule('sources_test_runner');
+  await TestRunner.showPanel('sources');
+  await TestRunner.navigatePromise('../sources/debugger-breakpoints/resources/dom-breakpoints.html');
+
+  TestRunner.addResult('Adding global listener.');
+  await TestRunner.evaluateInPagePromise(`window.addEventListener('touchstart', () => console.log);`);
+  await UI.viewManager.showView('sources.globalListeners');
+  const globalListenersPane = self.runtime.sharedInstance(BrowserDebugger.ObjectEventListenersSidebarPane);
+  const eventListenersView = globalListenersPane._eventListenersView;
+
+  TestRunner.addResult('Dumping event listeners view:');
+  await ElementsTestRunner.expandAndDumpEventListenersPromise(eventListenersView);
+  TestRunner.addResult('Running the axe-core linter on the global listeners pane.');
+  await AxeCoreTestRunner.runValidation(globalListenersPane.contentElement);
+  TestRunner.completeTest();
+})();
diff --git a/third_party/blink/web_tests/http/tests/security/agent-equality.html b/third_party/blink/web_tests/http/tests/security/agent-equality.html
index 3879557b..e5f2a63 100644
--- a/third_party/blink/web_tests/http/tests/security/agent-equality.html
+++ b/third_party/blink/web_tests/http/tests/security/agent-equality.html
@@ -93,8 +93,67 @@
   t.add_cleanup(() => { document.body.removeChild(iframe); });
 }, 'Documents on same-origin-but-different-ports should receive the same agent.');
 
-// TODO(yutak): Add tests for https and other tricky cases (data:// URLs, or
-// iframe srcdoc etc.).
+async_test(t => {
+  let iframe = document.createElement('iframe');
+  iframe.src =
+    'http://127.0.0.1:8000/security/resources/agent-equality-different-schemes.html';
+
+  window.addEventListener(
+    'message',
+    t.step_func(evt => {
+      if (evt.data[0] !== 'different-schemes test') {
+        return;
+      }
+      assert_equals(evt.data.length, 3);
+      assert_not_equals(evt.data[1], evt.data[2]);
+      t.done();
+    }));
+
+  document.body.appendChild(iframe);
+  t.add_cleanup(() => { document.body.removeChild(iframe); });
+}, 'Documents with different schemes (HTTP and HTTPS) should receive different agents.');
+
+async_test(t => {
+  let iframe = document.createElement('iframe');
+  iframe.src =
+    'http://127.0.0.1:8000/security/resources/agent-equality-data-url.html';
+
+  window.addEventListener(
+    'message',
+    t.step_func(evt => {
+      if (evt.data[0] !== 'data scheme test') {
+        return;
+      }
+      assert_equals(evt.data.length, 3);
+      assert_not_equals(evt.data[1], evt.data[2]);
+      t.done();
+    }));
+
+  document.body.appendChild(iframe);
+  t.add_cleanup(() => { document.body.removeChild(iframe); });
+}, 'Frame loaded as data: URL should receive a unique agent that is different from the parent\'s.');
+
+async_test(t => {
+  let iframe = document.createElement('iframe');
+  iframe.src =
+    'http://127.0.0.1:8000/security/resources/agent-equality-srcdoc.html';
+
+  window.addEventListener(
+    'message',
+    t.step_func(evt => {
+      if (evt.data[0] !== 'srcdoc iframe test') {
+        return;
+      }
+      assert_equals(evt.data.length, 3);
+      assert_equals(evt.data[1], evt.data[2]);
+      t.done();
+    }));
+
+  document.body.appendChild(iframe);
+  t.add_cleanup(() => { document.body.removeChild(iframe); });
+}, 'srcdoc iframe should receive the same agent as the parent\'s.');
+
+// TODO(yutak): Add tests that check agents after navigations.
 </script>
 </body>
 </html>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-data-url.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-data-url.html
new file mode 100644
index 0000000..3e0a3d82
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-data-url.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>data scheme test</title>
+</head>
+<body>
+<iframe src="data:text/html,&lt;script&gt;window.parent.postMessage(internals.getDocumentAgentId(document), '*');&lt;/script&gt;">
+</iframe>
+<script>
+// This test loads an iframe whose src attribute is a data: URL. It posts
+// it's agent ID to this frame, and we report the agent IDs of this frame and
+// the child iframe.
+//
+// Success condition: The agent IDs of this frame and the child frame are
+// different (the child frame should receive a unique agent because it's on
+// an opaque origin).
+
+function onMessage(evt) {
+  let iframeAgentId = evt.data;
+  let message = [
+    'data scheme test',
+    internals.getDocumentAgentId(document),
+    iframeAgentId
+  ];
+  window.parent.postMessage(message, '*');
+}
+
+window.addEventListener('message', onMessage, {'once': true});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-different-schemes.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-different-schemes.html
new file mode 100644
index 0000000..ebb1d8c
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-different-schemes.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Cross-origin test</title>
+</head>
+<body>
+<iframe src="https://127.0.0.1:8443/security/resources/agent-equality-report-to-parent.html">
+</iframe>
+<script>
+// This file should be loaded as an iframe hosted on 127.0.0.1:8000 on HTTP.
+
+// This test creates the following frame tree:
+//
+// A: http://127.0.0.1:8000 (this file)
+// |
+// +-- B: https://127.0.0.1:8443
+//
+// This page posts a message to the parent window, containing two strings
+// which correspond to the agent IDs of A and B, respectively.
+//
+// Success condition: A's agent is not the same as B's.
+
+function onMessage(evt) {
+  let agentIdB = evt.data;
+  let message = [
+    'different-schemes test',
+    internals.getDocumentAgentId(document),
+    agentIdB
+  ];
+  window.parent.postMessage(message, '*');
+}
+
+window.addEventListener('message', onMessage, {'once': true});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/http/tests/security/resources/agent-equality-srcdoc.html b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-srcdoc.html
new file mode 100644
index 0000000..be921d5
--- /dev/null
+++ b/third_party/blink/web_tests/http/tests/security/resources/agent-equality-srcdoc.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>srcdoc iframe test</title>
+</head>
+<body>
+<iframe srcdoc="&lt;script&gt;window.parent.postMessage(internals.getDocumentAgentId(document), '*');&lt;/script&gt;">
+</iframe>
+<script>
+// This test loads an iframe whose content is specified as its srcdoc
+// attribute. It posts it's agent ID to this frame, and we report the agent
+// IDs of this frame and the child iframe.
+//
+// Success condition: The agent IDs of this frame and the child frame are
+// the same (the child frame is same-origin as the parent frame, unlike the
+// data: URL's case. See:
+// https://html.spec.whatwg.org/C#determining-the-origin )
+
+function onMessage(evt) {
+  let iframeAgentId = evt.data;
+  let message = [
+    'srcdoc iframe test',
+    internals.getDocumentAgentId(document),
+    iframeAgentId
+  ];
+  window.parent.postMessage(message, '*');
+}
+
+window.addEventListener('message', onMessage, {'once': true});
+</script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/portals/portals-api.html b/third_party/blink/web_tests/portals/portals-api.html
deleted file mode 100644
index 3aa64eb2..0000000
--- a/third_party/blink/web_tests/portals/portals-api.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<title>Portals API test</title>
-<script src="../resources/testharness.js"></script>
-<script src="../resources/testharnessreport.js"></script>
-<body>
-  <script>
-    test(function() {
-      assert_true(document.createElement('portal') instanceof HTMLPortalElement);
-    }, "portal element exists")
-  </script>
-</body>
diff --git a/third_party/blink/web_tests/serial/resources/serial-test-utils.js b/third_party/blink/web_tests/serial/resources/serial-test-utils.js
index b82aedad..e2ebd2f 100644
--- a/third_party/blink/web_tests/serial/resources/serial-test-utils.js
+++ b/third_party/blink/web_tests/serial/resources/serial-test-utils.js
@@ -21,27 +21,28 @@
     assert_equals(actual[i], expected[i], `Mismatch at position ${i}.`);
 }
 
-// Pull from |reader| until it reports done and return the data as a combined
-// Uint8Array.
-async function readAll(reader) {
+// Pull from |reader| until at least |targetLength| is read or the stream
+// reports done. The data is returned as a combined Uint8Array.
+async function readWithLength(reader, targetLength) {
   const chunks = [];
+  let actualLength = 0;
+
   while (true) {
     let { value, done } = await reader.read();
-    if (done) {
+    chunks.push(value);
+    actualLength += value.byteLength;
+
+    if (actualLength >= targetLength || done) {
       // It would be better to allocate |buffer| up front with the number of
       // of bytes expected but this is the best that can be done without a BYOB
       // reader to control the amount of data read.
-      const length =
-          chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
-      const buffer = new Uint8Array(length);
+      const buffer = new Uint8Array(actualLength);
       chunks.reduce((offset, chunk) => {
         buffer.set(chunk, offset);
         return offset + chunk.byteLength;
       }, 0);
       return buffer;
     }
-
-    chunks.push(value);
   }
 }
 
@@ -139,17 +140,12 @@
     this.binding = new mojo.Binding(device.mojom.SerialPort,
                                     this, request);
     this.binding.setConnectionErrorHandler(() => {
-      // OS typically clears DTR on close.
-      this.outputSignals_.dtr = false;
-      this.writable_.getWriter().close();
-      this.binding = undefined;
+      this.close();
     });
   }
 
   write(data) {
-    let writer = this.writable_.getWriter();
-    writer.write(data);
-    writer.releaseLock();
+    this.writer_.write(data);
   }
 
   async read() {
@@ -160,7 +156,9 @@
   }
 
   simulateParityError() {
-    this.writable_.getWriter().close();
+    this.writer_.close();
+    this.writer_.releaseLock();
+    this.writer_ = undefined;
     this.writable_ = undefined;
     this.client_.onReadError(device.mojom.SerialReceiveError.PARITY_ERROR);
   }
@@ -191,6 +189,7 @@
     this.client_ = client;
     this.readable_ = new ReadableStream(new DataPipeSource(in_stream));
     this.writable_ = new WritableStream(new DataPipeSink(out_stream));
+    this.writer_ = this.writable_.getWriter();
     // OS typically sets DTR on open.
     this.outputSignals_.dtr = true;
     return { success: true };
@@ -200,6 +199,7 @@
 
   async clearReadError(out_stream) {
     this.writable_ = new WritableStream(new DataPipeSink(out_stream));
+    this.writer_ = this.writable_.getWriter();
     if (this.errorCleared_)
       this.errorCleared_();
   }
@@ -241,12 +241,17 @@
     };
   }
 
-  async setBreak() {
-    return { success: false };
-  }
-
-  async clearBreak() {
-    return { success: false };
+  async close() {
+    // OS typically clears DTR on close.
+    this.outputSignals_.dtr = false;
+    if (this.writer_) {
+      this.writer_.close();
+      this.writer_.releaseLock();
+      this.writer_ = undefined;
+    }
+    this.writable_ = undefined;
+    this.binding = undefined;
+    return {};
   }
 }
 
diff --git a/third_party/blink/web_tests/serial/serialPort_close.html b/third_party/blink/web_tests/serial/serialPort_close.html
index 8ab2884..37119d043b 100644
--- a/third_party/blink/web_tests/serial/serialPort_close.html
+++ b/third_party/blink/web_tests/serial/serialPort_close.html
@@ -10,15 +10,24 @@
 serial_test(async (t, fake) => {
   const { port, fakePort } = await getFakeSerialPort(fake);
 
-  port.close();
-}, 'A SerialPort can be closed if it was never opened.');
+  await promise_rejects(t, 'InvalidStateError', port.close());
+}, 'A SerialPort cannot be closed if it was never opened.');
 
 serial_test(async (t, fake) => {
   const { port, fakePort } = await getFakeSerialPort(fake);
 
   await port.open({ baudrate: 9600 });
-  port.close();
-  port.close();
-}, 'A SerialPort can be closed multiple times.');
+  await port.close();
+  await promise_rejects(t, 'InvalidStateError', port.close());
+}, 'A SerialPort cannot be closed if it is already closed.');
+
+serial_test(async (t, fake) => {
+  const { port, fakePort } = await getFakeSerialPort(fake);
+
+  await port.open({ baudrate: 9600 });
+  const closePromise = port.close();
+  await promise_rejects(t, 'InvalidStateError', port.close());
+  await closePromise;
+}, 'A SerialPort cannot be closed if it is being closed.');
 
 </script>
diff --git a/third_party/blink/web_tests/serial/serialPort_open.html b/third_party/blink/web_tests/serial/serialPort_open.html
index 585fd5e..ab92d77 100644
--- a/third_party/blink/web_tests/serial/serialPort_open.html
+++ b/third_party/blink/web_tests/serial/serialPort_open.html
@@ -7,15 +7,6 @@
 <script src="resources/serial-test-utils.js"></script>
 <script>
 
-// Waits for |port| to be marked closed by waiting for the ReadableStream to
-// close.
-async function closePortAndWait(port) {
-  const reader = port.readable.getReader();
-  port.close();
-  const { value, done } = await reader.read();
-  assert_true(done);
-}
-
 serial_test(async (t, fake) => {
   const { port, fakePort } = await getFakeSerialPort(fake);
 
@@ -52,7 +43,7 @@
   await [undefined, 7, 8].reduce(async (previousTest, databits) => {
     await previousTest;
     await port.open({ baudrate: 9600, databits });
-    await closePortAndWait(port);
+    await port.close();
   }, Promise.resolve());
 }, 'Data bits must be 7 or 8');
 
@@ -69,7 +60,7 @@
       async (previousTest, parity) => {
         await previousTest;
         await port.open({ baudrate: 9600, parity });
-        await closePortAndWait(port);
+        await port.close();
       }, Promise.resolve());
 }, 'Parity must be "none", "even" or "odd"');
 
@@ -84,7 +75,7 @@
   await [undefined, 1, 2].reduce(async (previousTest, stopbits) => {
     await previousTest;
     await port.open({ baudrate: 9600, stopbits });
-    await closePortAndWait(port);
+    await port.close();
   }, Promise.resolve());
 }, 'Stop bits must be 1 or 2');
 
diff --git a/third_party/blink/web_tests/serial/serialPort_readable.html b/third_party/blink/web_tests/serial/serialPort_readable.html
index 3b67e55..828e6ac 100644
--- a/third_party/blink/web_tests/serial/serialPort_readable.html
+++ b/third_party/blink/web_tests/serial/serialPort_readable.html
@@ -16,7 +16,7 @@
   const readable = port.readable;
   assert_true(readable instanceof ReadableStream);
 
-  port.close();
+  await port.close();
   assert_equals(port.readable, null);
 
   const reader = readable.getReader();
@@ -27,6 +27,20 @@
 
 serial_test(async (t, fake) => {
   const { port, fakePort } = await getFakeSerialPort(fake);
+
+  await port.open({ baudrate: 9600 });
+  assert_true(port.readable instanceof ReadableStream);
+
+  const reader = port.readable.getReader();
+  await promise_rejects(t, new TypeError(), port.close());
+
+  reader.releaseLock();
+  await port.close();
+  assert_equals(port.readable, null);
+}, 'Port cannot be closed while readable is locked');
+
+serial_test(async (t, fake) => {
+  const { port, fakePort } = await getFakeSerialPort(fake);
   // Select a buffer size larger than the amount of data transferred.
   await port.open({ baudrate: 9600, buffersize: 64 });
 
@@ -37,12 +51,9 @@
   let { value, done } = await reader.read();
   assert_false(done);
   compareArrays(data, value);
+  reader.releaseLock();
 
-  port.close();
-
-  ({ value, done } = await reader.read());
-  assert_true(done);
-  assert_equals(undefined, value);
+  await port.close();
 }, 'Can read a small amount of data');
 
 serial_test(async (t, fake) => {
@@ -56,9 +67,11 @@
   fakePort.write(data);
 
   const reader = port.readable.getReader();
-  port.close();
-  const value = await readAll(reader);
+  const value = await readWithLength(reader, data.byteLength);
   compareArrays(data, value);
+  reader.releaseLock();
+
+  await port.close();
 }, 'Can read a large amount of data');
 
 serial_test(async (t, fake) => {
@@ -89,13 +102,10 @@
   ({ value, done } = await reader.read());
   assert_false(done);
   compareArrays(data, value);
+  reader.releaseLock();
 
-  port.close();
+  await port.close();
   assert_equals(port.readable, null);
-
-  ({ value, done } = await reader.read());
-  assert_true(done);
-  assert_equals(undefined, value);
 }, 'Parity error closes readable and replaces it with a new stream');
 
 serial_test(async (t, fake) => {
@@ -109,12 +119,24 @@
   let { value, done } = await reader.read();
   assert_false(done);
   assert_equals("Hello world!", value);
+  await reader.cancel();
 
-  port.close();
+  await port.close();
+}, 'Can pipe readable through a transform stream.')
 
-  ({ value, done } = await reader.read());
+serial_test(async (t, fake) => {
+  const { port, fakePort } = await getFakeSerialPort(fake);
+  // Select a buffer size smaller than the amount of data transferred.
+  await port.open({ baudrate: 9600, buffersize: 64 });
+
+  const reader = port.readable.getReader();
+  const readPromise = reader.read();
+  await reader.cancel();
+  let { value, done } = await readPromise;
   assert_true(done);
   assert_equals(undefined, value);
-}, 'Can pipe readable through a transform stream.')
+
+  await port.close();
+}, 'Can cancel while reading');
 
 </script>
diff --git a/third_party/blink/web_tests/serial/serialPort_writable.html b/third_party/blink/web_tests/serial/serialPort_writable.html
index 6fa35d6c..4865b79 100644
--- a/third_party/blink/web_tests/serial/serialPort_writable.html
+++ b/third_party/blink/web_tests/serial/serialPort_writable.html
@@ -13,10 +13,15 @@
   assert_equals(port.writable, null);
 
   await port.open({ baudrate: 9600 });
-  assert_true(port.writable instanceof WritableStream);
+  const writable = port.writable;
+  assert_true(writable instanceof WritableStream);
 
-  port.close();
+  await port.close();
   assert_equals(port.writable, null);
+
+  const writer = writable.getWriter();
+  const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
+  await promise_rejects(t, 'InvalidStateError', writer.write(data));
 }, 'open() and close() set and unset SerialPort.writable');
 
 serial_test(async (t, fake) => {
@@ -26,12 +31,12 @@
   assert_true(port.writable instanceof WritableStream);
 
   const writer = port.writable.getWriter();
-  port.close();
+  await promise_rejects(t, new TypeError(), port.close());
 
-  const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
-  await promise_rejects(t, 'NetworkError', writer.write(data));
+  writer.releaseLock();
+  await port.close();
   assert_equals(port.writable, null);
-}, 'SerialPort.writable reports errors after close()');
+}, 'Port cannot be closed while writable is locked');
 
 serial_test(async (t, fake) => {
   const { port, fakePort } = await getFakeSerialPort(fake);
@@ -42,11 +47,12 @@
   const writer = port.writable.getWriter();
   const data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
   let writePromise = writer.write(data);
+  writer.close();
   let { value, done } = await fakePort.read();
   await writePromise;
   compareArrays(value, data);
 
-  port.close();
+  await port.close();
   assert_equals(port.writable, null);
 }, 'Can write a small amount of data');
 
@@ -63,9 +69,11 @@
   writer.close();
 
   const reader = fakePort.readable_.getReader();
-  const value = await readAll(reader);
+  const value = await readWithLength(reader, data.byteLength);
   reader.releaseLock();
   compareArrays(data, value);
+
+  await port.close();
 }, 'Can read a large amount of data');
 
 serial_test(async (t, fake) => {
@@ -75,15 +83,17 @@
   assert_true(port.writable instanceof WritableStream);
 
   const encoder = new TextEncoderStream();
-  encoder.readable.pipeTo(port.writable);
+  const encodingComplete = encoder.readable.pipeTo(port.writable);
   const writer = encoder.writable.getWriter();
   let writePromise = writer.write("Hello world!");
   let { value, done } = await fakePort.read();
   await writePromise;
   assert_equals("Hello world!", new TextDecoder().decode(value));
+  await writer.close();
+  await encodingComplete;
 
-  port.close();
-  assert_equals(null, port.writable);
+  await port.close();
+  assert_equals(port.writable, null);
 }, 'Can pipe a stream to writable');
 
 </script>
diff --git a/third_party/blink/web_tests/storage/indexeddb/blob-basics-metadata.html b/third_party/blink/web_tests/storage/indexeddb/blob-basics-metadata.html
index 0ee2f17..e513740 100644
--- a/third_party/blink/web_tests/storage/indexeddb/blob-basics-metadata.html
+++ b/third_party/blink/web_tests/storage/indexeddb/blob-basics-metadata.html
@@ -4,6 +4,9 @@
 <input type="file" id="fileInput" multiple></input>
 <script>
 
+ // This test cannot be a WPT because it uses the Blink specific APIs to
+ // automate file drag/drop.
+
 description("Confirm basic Blob/File/FileList functionality.");
 
 fileInput = document.getElementById("fileInput");
diff --git a/third_party/blink/web_tests/storage/indexeddb/blob-valid-after-deletion-expected.txt b/third_party/blink/web_tests/storage/indexeddb/blob-valid-after-deletion-expected.txt
deleted file mode 100644
index cac55aa2..0000000
--- a/third_party/blink/web_tests/storage/indexeddb/blob-valid-after-deletion-expected.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-Confirm that blobs stay alive after their records are deleted.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-dbname = "blob-valid-after-deletion.html"
-indexedDB.deleteDatabase(dbname)
-indexedDB.open(dbname)
-store = db.createObjectStore('store')
-store.put(value, key)
-
-
-doRead():
-trans = db.transaction('store')
-store = trans.objectStore('store')
-request = store.get(key)
-trans = db.transaction('store', 'readwrite')
-store = trans.objectStore('store')
-request = store.delete(key)
-PASS document.getElementById('frame0').contentDocument.body.innerText is blobAContent
-PASS document.getElementById('frame1').contentDocument.body.innerText is blobAContent
-PASS document.getElementById('frame2').contentDocument.body.innerText is blobBContent
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-  
diff --git a/third_party/blink/web_tests/storage/indexeddb/blob-valid-after-deletion.html b/third_party/blink/web_tests/storage/indexeddb/blob-valid-after-deletion.html
deleted file mode 100644
index c87902c..0000000
--- a/third_party/blink/web_tests/storage/indexeddb/blob-valid-after-deletion.html
+++ /dev/null
@@ -1,86 +0,0 @@
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-<script src="resources/shared.js"></script>
-</head>
-<body>
-<iframe id="frame0"></iframe>
-<iframe id="frame1"></iframe>
-<iframe id="frame2"></iframe>
-<script>
-
-description("Confirm that blobs stay alive after their records are deleted.");
-
-indexedDBTest(prepareDatabase, doRead);
-function prepareDatabase()
-{
-    db = event.target.result;
-    event.target.transaction.onabort = unexpectedAbortCallback;
-    evalAndLog("store = db.createObjectStore('store')");
-    blobAContent = "Blob A content";
-    blobBContent = "Blob B content";
-    var blobA = new Blob([blobAContent], {"type" : "text/plain"});
-    var blobB = new Blob([blobBContent], {"type" : "text/plain"});
-    key = "key"
-    value = { a0: blobA, a1: blobA, b0: blobB };
-    evalAndLog("store.put(value, key)");
-    value = null;
-}
-
-function doRead()
-{
-    preamble();
-    evalAndLog("trans = db.transaction('store')");
-    evalAndLog("store = trans.objectStore('store')");
-    evalAndLog("request = store.get(key)");
-    request.onsuccess = didRead;
-}
-
-function didRead()
-{
-    record = request.result;
-    trans.oncomplete = doDelete;
-}
-
-function doDelete()
-{
-    evalAndLog("trans = db.transaction('store', 'readwrite')");
-    evalAndLog("store = trans.objectStore('store')");
-    evalAndLog("request = store.delete(key)");
-    trans.oncomplete = didDelete;
-}
-
-function didDelete()
-{
-    urlA0 = URL.createObjectURL(record.a0);
-    urlA1 = URL.createObjectURL(record.a1);
-    urlB = URL.createObjectURL(record.b0);
-    document.getElementById('frame0').src = urlA0;
-    document.getElementById('frame0').onload = verification;
-    document.getElementById('frame1').src = urlA1;
-    document.getElementById('frame1').onload = verification;
-    document.getElementById('frame2').src = urlB;
-    document.getElementById('frame2').onload = verification;
-}
-
-var loadCount = 0;
-function verification()
-{
-    if (++loadCount < 3)
-        return;
-    URL.revokeObjectURL(urlA0);
-    URL.revokeObjectURL(urlA1);
-    URL.revokeObjectURL(urlB);
-    shouldBe("document.getElementById('frame0').contentDocument.body.innerText",
-        "blobAContent");
-    shouldBe("document.getElementById('frame1').contentDocument.body.innerText",
-        "blobAContent");
-    shouldBe("document.getElementById('frame2').contentDocument.body.innerText",
-        "blobBContent");
-    finishJSTest();
-}
-
-</script>
-</body>
-</html>
-
diff --git a/third_party/blink/web_tests/storage/indexeddb/blob-valid-before-commit-expected.txt b/third_party/blink/web_tests/storage/indexeddb/blob-valid-before-commit-expected.txt
deleted file mode 100644
index 6abcdd7..0000000
--- a/third_party/blink/web_tests/storage/indexeddb/blob-valid-before-commit-expected.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-Confirm that blobs can be read back before their records are committed.
-
-On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
-
-dbname = "blob-valid-before-commit.html"
-indexedDB.deleteDatabase(dbname)
-indexedDB.open(dbname)
-store = db.createObjectStore('store')
-store.put(value, key)
-request = store.get(key)
-PASS document.getElementById('frame0').contentDocument.body.innerText is blobAContent
-PASS document.getElementById('frame1').contentDocument.body.innerText is blobAContent
-PASS document.getElementById('frame2').contentDocument.body.innerText is blobBContent
-PASS successfullyParsed is true
-
-TEST COMPLETE
-
-  
diff --git a/third_party/blink/web_tests/storage/indexeddb/blob-valid-before-commit.html b/third_party/blink/web_tests/storage/indexeddb/blob-valid-before-commit.html
deleted file mode 100644
index b1ad207..0000000
--- a/third_party/blink/web_tests/storage/indexeddb/blob-valid-before-commit.html
+++ /dev/null
@@ -1,65 +0,0 @@
-<html>
-<head>
-<script src="../../resources/js-test.js"></script>
-<script src="resources/shared.js"></script>
-</head>
-<body>
-<iframe id="frame0"></iframe>
-<iframe id="frame1"></iframe>
-<iframe id="frame2"></iframe>
-<script>
-
-description("Confirm that blobs can be read back before their records are committed.");
-
-indexedDBTest(prepareDatabase);
-function prepareDatabase()
-{
-    db = event.target.result;
-    event.target.transaction.onabort = unexpectedAbortCallback;
-    evalAndLog("store = db.createObjectStore('store')");
-    blobAContent = "Blob A content";
-    blobBContent = "Blob B content";
-    var blobA = new Blob([blobAContent], {"type" : "text/plain"});
-    var blobB = new Blob([blobBContent], {"type" : "text/plain"});
-    key = "key"
-    value = { a0: blobA, a1: blobA, b0: blobB };
-    evalAndLog("store.put(value, key)");
-    evalAndLog("request = store.get(key)");
-    request.onsuccess = didRead;
-}
-
-function didRead()
-{
-    record = request.result;
-    urlA0 = URL.createObjectURL(record.a0);
-    urlA1 = URL.createObjectURL(record.a1);
-    urlB = URL.createObjectURL(record.b0);
-    document.getElementById('frame0').src = urlA0;
-    document.getElementById('frame0').onload = verification;
-    document.getElementById('frame1').src = urlA1;
-    document.getElementById('frame1').onload = verification;
-    document.getElementById('frame2').src = urlB;
-    document.getElementById('frame2').onload = verification;
-}
-
-var loadCount = 0;
-function verification()
-{
-    if (++loadCount < 3)
-        return;
-    URL.revokeObjectURL(urlA0);
-    URL.revokeObjectURL(urlA1);
-    URL.revokeObjectURL(urlB);
-    shouldBe("document.getElementById('frame0').contentDocument.body.innerText",
-        "blobAContent");
-    shouldBe("document.getElementById('frame1').contentDocument.body.innerText",
-        "blobAContent");
-    shouldBe("document.getElementById('frame2').contentDocument.body.innerText",
-        "blobBContent");
-    finishJSTest();
-}
-
-</script>
-</body>
-</html>
-
diff --git a/third_party/blink/web_tests/storage/indexeddb/empty-blob-file.html b/third_party/blink/web_tests/storage/indexeddb/empty-blob-file.html
index 599250dd..1daef10 100644
--- a/third_party/blink/web_tests/storage/indexeddb/empty-blob-file.html
+++ b/third_party/blink/web_tests/storage/indexeddb/empty-blob-file.html
@@ -5,6 +5,9 @@
 <input type="file" id="emptyFileListInput" multiple></input>
 <script>
 
+ // This test cannot be a WPT because it uses the Blink specific APIs to
+ // automate file drag/drop.
+
 description("Confirm that IndexedDB can store an empty Blob/File/FileList");
 
 var emptyFileInput = document.getElementById("emptyFileInput");
diff --git a/tools/android/avd/proto/generic_android23.textpb b/tools/android/avd/proto/generic_android23.textpb
index 00aec6d..d99086f 100644
--- a/tools/android/avd/proto/generic_android23.textpb
+++ b/tools/android/avd/proto/generic_android23.textpb
@@ -19,7 +19,7 @@
 
 avd_package {
   package_name: "chromium/third_party/android_sdk/public/avds/android-23/google_apis/x86"
-  version: "E4QZINcjPpJ-j6SUPCJKGwVBD_jQY--F4sGiIWUPkeoC"
+  version: "1pPLFfxM1_EUbHzIa0eB4LBgN6Kb46JyAon7V4cCew4C"
   dest_path: ".android"
 }
 avd_name: "android_23_google_apis_x86"
diff --git a/tools/android/avd/proto/generic_android28.textpb b/tools/android/avd/proto/generic_android28.textpb
index fe20974..9eb874a 100644
--- a/tools/android/avd/proto/generic_android28.textpb
+++ b/tools/android/avd/proto/generic_android28.textpb
@@ -19,7 +19,7 @@
 
 avd_package {
   package_name: "chromium/third_party/android_sdk/public/avds/android-28/google_apis/x86"
-  version: "LZ3LVXWiSLrfF6pHjuQ_LYHdQDoMGnqU1TuCmjp7UTAC"
+  version: "881VikpWEVRNBUND7arr_fnsBakWARQTvkAPdl9lNIoC"
   dest_path: ".android"
 }
 avd_name: "android_28_google_apis_x86"
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 26b9699..b97b95f 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -35044,7 +35044,6 @@
   <int value="-1998927516" label="enable-md-settings"/>
   <int value="-1996233283"
       label="OmniboxGroupSuggestionsBySearchVsUrl:enabled"/>
-  <int value="-1992319691" label="AvoidFlashBetweenNavigation:enabled"/>
   <int value="-1990614981" label="StoragePressureUI:disabled"/>
   <int value="-1989747818" label="TabStripKeyboardFocus:disabled"/>
   <int value="-1985452239" label="SmartDim20190221:disabled"/>
@@ -35899,6 +35898,7 @@
   <int value="-950384924" label="OmniboxDisableInstantExtendedLimit:enabled"/>
   <int value="-949178861" label="enable-new-avatar-menu"/>
   <int value="-945806012" label="DownloadsUi:enabled"/>
+  <int value="-943304570" label="PaintHolding:enabled"/>
   <int value="-938178614" label="enable-suggestions-with-substring-match"/>
   <int value="-933377608"
       label="OmniboxUIExperimentHideSteadyStateUrlScheme:enabled"/>
@@ -36274,6 +36274,7 @@
   <int value="-450976085"
       label="AutofillSaveCreditCardUsesImprovedMessaging:disabled"/>
   <int value="-449465495" label="disable-browser-task-scheduler"/>
+  <int value="-444867364" label="Metal:enabled"/>
   <int value="-438379844" label="SwapSideVolumeButtonsForOrientation:enabled"/>
   <int value="-436470115" label="TouchpadAndWheelScrollLatching:enabled"/>
   <int value="-435914745" label="ClipboardContentSetting:disabled"/>
@@ -36447,7 +36448,6 @@
   <int value="-195029497" label="MediaRemoting:disabled"/>
   <int value="-192919826" label="ViewsSimplifiedFullscreenUI:enabled"/>
   <int value="-192389983" label="NoStatePrefetch:enabled"/>
-  <int value="-191256027" label="AvoidFlashBetweenNavigation:disabled"/>
   <int value="-189478545" label="ArcCustomTabsExperiment:enabled"/>
   <int value="-186707397" label="HideArcMediaNotifications:enabled"/>
   <int value="-185162926" label="IncreaseInputAudioBufferSize:enabled"/>
@@ -37161,6 +37161,7 @@
   <int value="740056959" label="ImeServiceConnectable:enabled"/>
   <int value="740619684" label="ExtensionsToolbarMenu:enabled"/>
   <int value="742083923" label="MimeHandlerViewInCrossProcessFrame:disabled"/>
+  <int value="745541471" label="PaintHolding:disabled"/>
   <int value="745783589" label="translate-force-trigger-on-english"/>
   <int value="745868416" label="disable-system-timezone-automatic-detection"/>
   <int value="746765012" label="AutofillEnableToolbarStatusChip:disabled"/>
@@ -37568,6 +37569,7 @@
   <int value="1308537004" label="force-pnacl-subzero"/>
   <int value="1311860720" label="ChromeHomeNtpRedesign:disabled"/>
   <int value="1312025202" label="NTPOfflinePageSuggestions:disabled"/>
+  <int value="1313850691" label="Metal:disabled"/>
   <int value="1314681756" label="NoStatePrefetch:disabled"/>
   <int value="1317562265" label="SeccompSandboxAndroid:disabled"/>
   <int value="1318073661" label="MaterialDesignExtensions:enabled"/>
@@ -49820,6 +49822,8 @@
   <int value="18" label="The navigation URL had an excluded media suffix."/>
   <int value="19" label="Not allowed by server rules provided to the client."/>
   <int value="20" label="Coin flip holdback encountered."/>
+  <int value="21" label="Redirect loop detected."/>
+  <int value="22" label="URL matched deny list."/>
 </enum>
 
 <enum name="PreviewsHintCacheLevelDBStoreLoadMetadataResult">
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index bc36d8d..4b0fc46 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -6497,6 +6497,46 @@
   </summary>
 </histogram>
 
+<histogram name="Arc.IconLoadFromFileTime.compressedFirst5" units="ms"
+    expires_after="2020-03-01">
+  <owner>khmel@google.com</owner>
+  <owner>lgcheng@google.com</owner>
+  <summary>
+    Elapsed time of first 5 compressed app icons is loaded from file system.
+    Recorded when the app icon loading completes.
+  </summary>
+</histogram>
+
+<histogram name="Arc.IconLoadFromFileTime.compressedOthers" units="ms"
+    expires_after="2020-03-01">
+  <owner>khmel@google.com</owner>
+  <owner>lgcheng@google.com</owner>
+  <summary>
+    Elapsed time of other compressed app icons is loaded from file system.
+    Recorded when the app icon loading completes.
+  </summary>
+</histogram>
+
+<histogram name="Arc.IconLoadFromFileTime.uncompressedFirst5" units="ms"
+    expires_after="2020-03-01">
+  <owner>khmel@google.com</owner>
+  <owner>lgcheng@google.com</owner>
+  <summary>
+    Elapsed time of first 5 uncompressed app icons is loaded from file system.
+    Recorded when the app icon loading completes.
+  </summary>
+</histogram>
+
+<histogram name="Arc.IconLoadFromFileTime.uncompressedOthers" units="ms"
+    expires_after="2020-03-01">
+  <owner>khmel@google.com</owner>
+  <owner>lgcheng@google.com</owner>
+  <summary>
+    Elapsed time of other uncompressed app icons is loaded from file system.
+    Recorded when the app icon loading completes.
+  </summary>
+</histogram>
+
 <histogram name="Arc.ImeCount" units="units" expires_after="M81">
   <owner>yhanada@google.com</owner>
   <owner>tetsui@google.com</owner>
diff --git a/tools/perf/core/results_processor/processor.py b/tools/perf/core/results_processor/processor.py
index d16a0c7..4e22cd5f 100644
--- a/tools/perf/core/results_processor/processor.py
+++ b/tools/perf/core/results_processor/processor.py
@@ -20,6 +20,7 @@
 from core.results_processor import formatters
 from core.results_processor import util
 
+from tracing.trace_data import trace_data
 from tracing.value.diagnostics import generic_set
 from tracing.value.diagnostics import reserved_infos
 from tracing.value import histogram_set
@@ -46,7 +47,7 @@
   intermediate_results = _LoadIntermediateResults(
       os.path.join(options.intermediate_dir, TELEMETRY_RESULTS))
 
-  _AggregateTraces(intermediate_results)
+  AggregateTraces(intermediate_results)
 
   UploadArtifacts(
       intermediate_results, options.upload_bucket, options.results_label)
@@ -99,7 +100,7 @@
   return results
 
 
-def _AggregateTraces(intermediate_results):
+def AggregateTraces(intermediate_results):
   """Replace individual traces with an aggregate one for each test result.
 
   For each test run with traces, generates an aggregate HTML trace. Removes
@@ -109,10 +110,16 @@
     artifacts = result.get('outputArtifacts', {})
     traces = [name for name in artifacts if name.startswith('trace/')]
     if len(traces) > 0:
-      # For now, the html trace is generated by Telemetry, so it should be there
-      # already. All we need to do is remove individual traces from the dict.
-      # TODO(crbug.com/981349): replace this with actual aggregation code.
-      assert compute_metrics.HTML_TRACE_NAME in artifacts
+      if compute_metrics.HTML_TRACE_NAME not in artifacts:
+        trace_files = [artifacts[name]['filePath'] for name in traces]
+        html_path = os.path.join(
+            os.path.dirname(os.path.commonprefix(trace_files)),
+            compute_metrics.HTML_TRACE_NAME)
+        trace_data.SerializeAsHtml(trace_files, html_path)
+        artifacts[compute_metrics.HTML_TRACE_NAME] = {
+          'filePath': html_path,
+          'contentType': 'text/html',
+        }
       for trace in traces:
         del artifacts[trace]
 
diff --git a/tools/perf/core/results_processor/processor_test.py b/tools/perf/core/results_processor/processor_test.py
index a755845..b25893d4 100644
--- a/tools/perf/core/results_processor/processor_test.py
+++ b/tools/perf/core/results_processor/processor_test.py
@@ -24,6 +24,7 @@
 from core.results_processor import processor
 from core.results_processor import testing
 
+from tracing.value.diagnostics import generic_set
 from tracing.value import histogram
 from tracing.value import histogram_set
 from tracing_build import render_histograms_viewer
@@ -268,12 +269,45 @@
 
     out_histograms = histogram_set.HistogramSet()
     out_histograms.ImportDicts(results)
-    self.assertEqual(len(out_histograms), 4)
-    self.assertIsNotNone(out_histograms.GetHistogramNamed('foo'))
 
-    diag_values = [list(v) for v in  out_histograms.shared_diagnostics]
-    self.assertEqual(len(diag_values), 1)
-    self.assertIn(['gs://trace.html'], diag_values)
+    # sampleMetric records a histogram with the name 'foo'.
+    hist = out_histograms.GetHistogramNamed('foo')
+    self.assertIsNotNone(hist)
+    self.assertEqual(hist.diagnostics['traceUrls'],
+                     generic_set.GenericSet(['gs://trace.html']))
+
+  def testHistogramsOutputNoAggregatedTrace(self):
+    json_trace = os.path.join(self.output_dir, 'trace.json')
+    with open(json_trace, 'w') as f:
+      json.dump({'traceEvents': []}, f)
+
+    self.SerializeIntermediateResults(
+        test_results=[
+            testing.TestResult(
+                'benchmark/story',
+                output_artifacts={'trace/json': testing.Artifact(json_trace)},
+                tags=['tbmv2:sampleMetric'],
+            ),
+        ],
+    )
+
+    processor.main([
+        '--output-format', 'histograms',
+        '--output-dir', self.output_dir,
+        '--intermediate-dir', self.intermediate_dir,
+    ])
+
+    with open(os.path.join(
+        self.output_dir, histograms_output.OUTPUT_FILENAME)) as f:
+      results = json.load(f)
+
+    out_histograms = histogram_set.HistogramSet()
+    out_histograms.ImportDicts(results)
+
+    # sampleMetric records a histogram with the name 'foo'.
+    hist = out_histograms.GetHistogramNamed('foo')
+    self.assertIsNotNone(hist)
+    self.assertIn('traceUrls', hist.diagnostics)
 
   def testHtmlOutput(self):
     hist_file = os.path.join(self.output_dir,
diff --git a/tools/perf/core/results_processor/processor_unittest.py b/tools/perf/core/results_processor/processor_unittest.py
index 192f37e..d2de1e5 100644
--- a/tools/perf/core/results_processor/processor_unittest.py
+++ b/tools/perf/core/results_processor/processor_unittest.py
@@ -94,3 +94,55 @@
             'src_abc_123_20191001T120000_54321/benchmark/story/trace.html',
             '/trace.html'
         )
+
+  def testAggregateTraces(self):
+    in_results = testing.IntermediateResults(
+        test_results=[
+            testing.TestResult(
+                'benchmark/story1',
+                output_artifacts={
+                    'trace/1.json': testing.Artifact(
+                        '/artifacts/test_run/story1/trace/1.json'),
+                },
+            ),
+            testing.TestResult(
+                'benchmark/story2',
+                output_artifacts={
+                    'trace/1.json': testing.Artifact(
+                        '/artifacts/test_run/story2/trace/1.json'),
+                    'trace/2.json': testing.Artifact(
+                        '/artifacts/test_run/story2/trace/2.json'),
+                },
+            ),
+        ],
+    )
+
+    with mock.patch('tracing.trace_data.trace_data.SerializeAsHtml') as patch:
+      processor.AggregateTraces(in_results)
+
+    call_list = [list(call[0]) for call in patch.call_args_list]
+    self.assertEqual(len(call_list), 2)
+    for call in call_list:
+      call[0] = set(call[0])
+    self.assertIn(
+        [
+            set(['/artifacts/test_run/story1/trace/1.json']),
+            '/artifacts/test_run/story1/trace/trace.html',
+        ],
+        call_list
+    )
+    self.assertIn(
+        [
+            set([
+                '/artifacts/test_run/story2/trace/1.json',
+                '/artifacts/test_run/story2/trace/2.json',
+            ]),
+            '/artifacts/test_run/story2/trace/trace.html',
+        ],
+        call_list
+    )
+
+    for result in in_results['testResults']:
+      artifacts = result['outputArtifacts']
+      self.assertEqual(len(artifacts), 1)
+      self.assertEqual(artifacts.keys()[0], 'trace.html')
diff --git a/tools/traffic_annotation/auditor/auditor_result.cc b/tools/traffic_annotation/auditor/auditor_result.cc
index 2735088..48f65b62 100644
--- a/tools/traffic_annotation/auditor/auditor_result.cc
+++ b/tools/traffic_annotation/auditor/auditor_result.cc
@@ -31,7 +31,8 @@
          type == AuditorResult::Type::ERROR_MISSING_SECOND_ID ||
          type == AuditorResult::Type::ERROR_DIRECT_ASSIGNMENT ||
          type == AuditorResult::Type::ERROR_TEST_ANNOTATION ||
-         type == AuditorResult::Type::ERROR_INVALID_OS);
+         type == AuditorResult::Type::ERROR_INVALID_OS ||
+         type == AuditorResult::Type::ERROR_MUTABLE_TAG);
   if (!message.empty())
     details_.push_back(message);
 }
diff --git a/ui/compositor/layer.cc b/ui/compositor/layer.cc
index f992a25..ef01ac9 100644
--- a/ui/compositor/layer.cc
+++ b/ui/compositor/layer.cc
@@ -1292,6 +1292,10 @@
   return value;
 }
 
+std::string Layer::LayerDebugName(const cc::Layer* layer) const {
+  return name_;
+}
+
 void Layer::DidChangeScrollbarsHiddenIfOverlay(bool) {}
 
 void Layer::CollectAnimators(
diff --git a/ui/compositor/layer.h b/ui/compositor/layer.h
index 80814b1..1abb661 100644
--- a/ui/compositor/layer.h
+++ b/ui/compositor/layer.h
@@ -466,6 +466,7 @@
   // LayerClient implementation.
   std::unique_ptr<base::trace_event::TracedValue> TakeDebugInfo(
       const cc::Layer* layer) override;
+  std::string LayerDebugName(const cc::Layer* layer) const override;
   void DidChangeScrollbarsHiddenIfOverlay(bool) override;
 
   // Triggers a call to SwitchToLayer.
diff --git a/ui/gfx/platform_font_win.cc b/ui/gfx/platform_font_win.cc
index c83538b..f61037e 100644
--- a/ui/gfx/platform_font_win.cc
+++ b/ui/gfx/platform_font_win.cc
@@ -275,11 +275,27 @@
 PlatformFontWin::PlatformFontWin() : font_ref_(GetBaseFontRef()) {
 }
 
-PlatformFontWin::PlatformFontWin(const std::string& font_name,
-                                 int font_size) {
+PlatformFontWin::PlatformFontWin(const std::string& font_name, int font_size) {
   InitWithFontNameAndSize(font_name, font_size);
 }
 
+PlatformFontWin::PlatformFontWin(sk_sp<SkTypeface> typeface,
+                                 int font_size_pixels,
+                                 const base::Optional<FontRenderParams>& params)
+    : typeface_(std::move(typeface)) {
+  DCHECK(typeface_);
+
+  // TODO(http://crbug.com/944227): This is a transitional code path until we
+  // complete migrating to PlatformFontSkia on Windows. Being unable to wrap the
+  // SkTypeface into a PlatformFontSkia and performing a rematching by font
+  // family name instead loses platform font handles encapsulated in SkTypeface,
+  // and in turn leads to instantiating a different font than what was returned
+  // by font fallback, compare https://crbug.com/1003829.
+  SkString family_name;
+  typeface_->getFamilyName(&family_name);
+  InitWithFontNameAndSize(family_name.c_str(), font_size_pixels);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // PlatformFontWin, PlatformFont implementation:
 
@@ -360,7 +376,7 @@
 }
 
 sk_sp<SkTypeface> PlatformFontWin::GetNativeSkTypefaceIfAvailable() const {
-  return nullptr;
+  return sk_sp<SkTypeface>(typeface_);
 }
 
 NativeFont PlatformFontWin::GetNativeFont() const {
@@ -657,16 +673,7 @@
   TRACE_EVENT0("fonts", "PlatformFont::CreateFromSkTypeface");
   if (base::FeatureList::IsEnabled(kPlatformFontSkiaOnWindows))
     return new PlatformFontSkia(typeface, font_size, params);
-
-  // This is a transitional code path until we complete migrating to
-  // PlatformFontSkia on Windows. Being unable to wrap the SkTypeface into a
-  // PlatformFontSkia and performing a rematching by font family name instead
-  // loses platform font handles encapsulated in SkTypeface, and in turn leads
-  // to instantiating a different font than what was returned by font fallback,
-  // compare https://crbug.com/1003829.
-  SkString family_name;
-  typeface->getFamilyName(&family_name);
-  return new PlatformFontWin(family_name.c_str(), font_size);
+  return new PlatformFontWin(typeface, font_size, params);
 }
 
 }  // namespace gfx
diff --git a/ui/gfx/platform_font_win.h b/ui/gfx/platform_font_win.h
index 47eeebb..a114af6 100644
--- a/ui/gfx/platform_font_win.h
+++ b/ui/gfx/platform_font_win.h
@@ -13,6 +13,7 @@
 #include "base/gtest_prod_util.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
+#include "third_party/skia/include/core/SkTypeface.h"
 #include "ui/gfx/gfx_export.h"
 #include "ui/gfx/platform_font.h"
 
@@ -26,6 +27,11 @@
   PlatformFontWin();
   PlatformFontWin(const std::string& font_name, int font_size);
 
+  // Wraps the provided SkTypeface without triggering a font rematch.
+  PlatformFontWin(sk_sp<SkTypeface> typeface,
+                  int font_size_pixels,
+                  const base::Optional<FontRenderParams>& params);
+
   // Dialog units to pixels conversion.
   // See http://support.microsoft.com/kb/145994 for details.
   int horizontal_dlus_to_pixels(int dlus) const {
@@ -191,6 +197,9 @@
   // Indirect reference to the HFontRef, which references the underlying HFONT.
   scoped_refptr<HFontRef> font_ref_;
 
+  // An optional typeface when the font is constructed from a typeface.
+  sk_sp<SkTypeface> typeface_;
+
   DISALLOW_COPY_AND_ASSIGN(PlatformFontWin);
 };
 
diff --git a/ui/gfx/platform_font_win_unittest.cc b/ui/gfx/platform_font_win_unittest.cc
index 8dd4d0a..f19efeb 100644
--- a/ui/gfx/platform_font_win_unittest.cc
+++ b/ui/gfx/platform_font_win_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/win/scoped_hdc.h"
 #include "base/win/scoped_select_object.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkFontMgr.h"
 #include "ui/gfx/font.h"
 #include "ui/gfx/font_render_params.h"
 #include "ui/gfx/platform_font_skia.h"
@@ -143,4 +144,18 @@
             named_font->GetFontRenderParams());
 }
 
+TEST(PlatformFontWinTest, SkiaTypefaceConstructor) {
+  gfx::Font default_font;
+  EXPECT_EQ(default_font.platform_font()->GetNativeSkTypefaceIfAvailable(),
+            nullptr);
+
+  sk_sp<SkFontMgr> font_mgr = SkFontMgr::RefDefault();
+  sk_sp<SkTypeface> typeface(
+      font_mgr->matchFamilyStyle("Segoe UI", SkFontStyle()));
+  ASSERT_TRUE(typeface);
+  gfx::Font fallback_font(new PlatformFontWin(typeface, 13, base::nullopt));
+  EXPECT_EQ(fallback_font.platform_font()->GetNativeSkTypefaceIfAvailable(),
+            typeface);
+}
+
 }  // namespace gfx
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.html b/ui/webui/resources/cr_components/chromeos/network/network_config.html
index d355c09..f0adbb8 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.html
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.html
@@ -35,7 +35,7 @@
       <!-- SSID (WiFi) -->
       <template is="dom-if" if="[[isWiFi_(mojoType_)]]" restamp>
         <network-config-input id="ssid" label="[[i18n('OncWiFi-SSID')]]"
-            value="{{configProperties_.wifi.ssid}}"
+            value="{{configProperties_.typeConfig.wifi.ssid}}"
             readonly="[[hasGuid_(guid)]]"
             on-enter="onEnterPressedInInput_">
         </network-config-input>
@@ -58,7 +58,7 @@
           if="[[configRequiresPassphrase_(mojoType_, securityType)]]">
         <network-password-input id="wifi-passphrase"
             label="[[i18n('OncWiFi-Passphrase')]]"
-            value="{{configProperties_.wifi.passphrase}}"
+            value="{{configProperties_.typeConfig.wifi.passphrase}}"
             on-enter="onEnterPressedInInput_"
             property="[[managedProperties_.typeProperties.wifi.passphrase]]">
         </network-password-input>
@@ -67,7 +67,7 @@
       <!-- VPN -->
       <template is="dom-if" if="[[showVpn_]]" restamp>
         <network-config-input label="[[i18n('OncVPN-Host')]]"
-            value="{{configProperties_.vpn.host}}"
+            value="{{configProperties_.typeConfig.vpn.host}}"
             property="[[managedProperties_.typeProperties.vpn.host]]">
         </network-config-input>
         <network-config-input label="[[i18n('OncName')]]"
@@ -81,37 +81,37 @@
         </network-config-select>
         <template is="dom-if" if="[[!showVpn_.OpenVPN]]">
           <network-config-input label="[[i18n('OncVPN-L2TP-Username')]]"
-              value="{{configProperties_.vpn.l2tp.username}}"
+              value="{{configProperties_.typeConfig.vpn.l2tp.username}}"
               property="[[managedProperties_.typeProperties.vpn.l2tp.username]]"
           >
           </network-config-input>
           <network-password-input label="[[i18n('OncVPN-L2TP-Password')]]"
-              value="{{configProperties_.vpn.l2tp.password}}"
+              value="{{configProperties_.typeConfig.vpn.l2tp.password}}"
               property="[[managedProperties_.typeProperties.vpn.l2tp.password]]"
           >
           </network-password-input>
           <network-config-input label="[[i18n('OncVPN-IPsec-Group')]]"
-              value="{{configProperties_.vpn.ipSec.group}}"
+              value="{{configProperties_.typeConfig.vpn.ipSec.group}}"
               property="[[managedProperties_.typeProperties.vpn.ipSec.group]]">
           </network-config-input>
           <template is="dom-if" if="[[!showVpn_.Cert]]">
             <network-password-input label="[[i18n('OncVPN-IPsec-PSK')]]"
-                value="{{configProperties_.vpn.ipSec.psk}}"
+                value="{{configProperties_.typeConfig.vpn.ipSec.psk}}"
                 property="[[managedProperties_.typeProperties.vpn.ipSec.psk]]">
             </network-password-input>
           </template>
         </template>
         <template is="dom-if" if="[[showVpn_.OpenVPN]]">
           <network-config-input label="[[i18n('OncVPN-OpenVPN-Username')]]"
-              value="{{configProperties_.vpn.openVpn.username}}"
+              value="{{configProperties_.typeConfig.vpn.openVpn.username}}"
               property="[[managedProperties_.typeProperties.vpn.openVpn.username]]">
           </network-config-input>
           <network-password-input label="[[i18n('OncVPN-OpenVPN-Password')]]"
-              value="{{configProperties_.vpn.openVpn.password}}"
+              value="{{configProperties_.typeConfig.vpn.openVpn.password}}"
               property="[[managedProperties_.typeProperties.vpn.openVpn.password]]">
           </network-password-input>
           <network-config-input label="[[i18n('OncVPN-OpenVPN-OTP')]]"
-              value="{{configProperties_.vpn.openVpn.otp}}"
+              value="{{configProperties_.typeConfig.vpn.openVpn.otp}}"
               property="[[managedProperties_.typeProperties.vpn.openVpn.otp]]">
           </network-config-input>
         </template>
diff --git a/ui/webui/resources/cr_components/chromeos/network/network_config.js b/ui/webui/resources/cr_components/chromeos/network/network_config.js
index 3d5eeab..31b1e58 100644
--- a/ui/webui/resources/cr_components/chromeos/network/network_config.js
+++ b/ui/webui/resources/cr_components/chromeos/network/network_config.js
@@ -315,13 +315,13 @@
     'updateEapCerts_(eapProperties_.*, serverCaCerts_, userCerts_)',
     'updateShowEap_(configProperties_.*, eapProperties_.*, securityType)',
     'updateVpnType_(configProperties_, vpnType_)',
-    'updateVpnIPsecCerts_(vpnType_, configProperties_.vpn.ipSec.*)',
-    'updateOpenVPNCerts_(vpnType_, configProperties_.vpn.openVpn.*)',
+    'updateVpnIPsecCerts_(vpnType_, configProperties_.typeConfig.vpn.ipSec.*)',
+    'updateOpenVPNCerts_(vpnType_, configProperties_.typeConfig.vpn.openVpn.*)',
     // Multiple updateIsConfigured observers for different configurations.
     'updateIsConfigured_(configProperties_.*, securityType)',
     'updateIsConfigured_(configProperties_, eapProperties_.*)',
-    'updateIsConfigured_(configProperties_.wifi.*)',
-    'updateIsConfigured_(configProperties_.vpn.*, vpnType_)',
+    'updateIsConfigured_(configProperties_.typeConfig.wifi.*)',
+    'updateIsConfigured_(configProperties_.typeConfig.vpn.*, vpnType_)',
     'updateIsConfigured_(selectedUserCertHash_)',
   ],
 
@@ -480,7 +480,7 @@
   onNetworkCertificatesChanged: function() {
     this.networkConfig_.getNetworkCertificates().then(response => {
       const isOpenVpn = this.configProperties_.type == mojom.NetworkType.kVPN &&
-          this.configProperties_.vpn.type == mojom.VpnType.kOpenVPN;
+          this.configProperties_.typeConfig.vpn.type == mojom.VpnType.kOpenVPN;
 
       const caCerts = response.serverCas.slice();
       if (!isOpenVpn) {
@@ -742,28 +742,25 @@
     this.vpnType_ = undefined;
 
     const managedProperties = this.managedProperties_;
-    const configProperties = {
-      name: OncMojo.getActiveString(managedProperties.name),
-      type: managedProperties.type,
-    };
+    const configProperties =
+        OncMojo.getDefaultConfigProperties(managedProperties.type);
+    configProperties.name = OncMojo.getActiveString(managedProperties.name);
+
     let autoConnect;
     let security = mojom.SecurityType.kNone;
     switch (managedProperties.type) {
       case mojom.NetworkType.kWiFi:
-        autoConnect = this.getActiveBoolean_(
-            managedProperties.typeProperties.wifi.autoConnect);
-        configProperties.wifi = {
-          passphrase: '',
-          ssid: OncMojo.getActiveString(
-              managedProperties.typeProperties.wifi.ssid),
-          security: managedProperties.typeProperties.wifi.security,
-        };
-        if (managedProperties.typeProperties.wifi.eap) {
-          configProperties.wifi.eap = this.getEAPConfigProperties_(
-              managedProperties.typeProperties.wifi.eap);
+        const wifi = managedProperties.typeProperties.wifi;
+        const configWifi = configProperties.typeConfig.wifi;
+        autoConnect = this.getActiveBoolean_(wifi.autoConnect);
+        configWifi.passphrase = '';
+        configWifi.ssid = OncMojo.getActiveString(wifi.ssid);
+        if (wifi.eap) {
+          configWifi.eap = this.getEAPConfigProperties_(wifi.eap);
         }
-        security = configProperties.wifi.security;
         // updateSecurity_ will ensure that EAP properties are set correctly.
+        security = wifi.security;
+        configWifi.security = security;
         break;
       case mojom.NetworkType.kEthernet:
         autoConnect = this.getActiveBoolean_(
@@ -774,29 +771,24 @@
             undefined;
         security = eap ? mojom.SecurityType.kWpaEap : mojom.SecurityType.kNone;
         const auth = security == mojom.SecurityType.kWpaEap ? '8021X' : 'None';
-        configProperties.ethernet = {
-          authentication: auth,
-          eap: eap,
-        };
+        configProperties.typeConfig.ethernet.authentication = auth;
+        configProperties.typeConfig.ethernet.eap = eap;
         break;
       case mojom.NetworkType.kVPN:
         const vpn = managedProperties.typeProperties.vpn;
         const vpnType = vpn.type;
-        configProperties.vpn = {
-          host: OncMojo.getActiveString(vpn.host),
-          type: vpnType,
-        };
+        const configVpn = configProperties.typeConfig.vpn;
+        configVpn.host = OncMojo.getActiveString(vpn.host);
+        configVpn.type = vpnType;
         if (vpnType == mojom.VpnType.kL2TPIPsec) {
           assert(vpn.ipSec);
-          configProperties.vpn.ipSec =
-              this.getIPSecConfigProperties_(vpn.ipSec);
+          configVpn.ipSec = this.getIPSecConfigProperties_(vpn.ipSec);
           assert(vpn.l2tp);
-          configProperties.vpn.l2tp = this.getL2TPConfigProperties_(vpn.l2tp);
+          configVpn.l2tp = this.getL2TPConfigProperties_(vpn.l2tp);
         } else {
           assert(vpnType == mojom.VpnType.kOpenVPN);
           assert(vpn.openVpn);
-          configProperties.vpn.openVpn =
-              this.getOpenVPNConfigProperties_(vpn.openVpn);
+          configVpn.openVpn = this.getOpenVPNConfigProperties_(vpn.openVpn);
         }
         security = mojom.SecurityType.kNone;
         break;
@@ -827,10 +819,10 @@
     const type = this.mojoType_;
     const security = this.securityType;
     if (type == mojom.NetworkType.kWiFi) {
-      this.configProperties_.wifi.security = security;
+      this.configProperties_.typeConfig.wifi.security = security;
     } else if (type == mojom.NetworkType.kEthernet) {
       const auth = security == mojom.SecurityType.kWpaEap ? '8021X' : 'None';
-      this.configProperties_.ethernet.authentication = auth;
+      this.configProperties_.typeConfig.ethernet.authentication = auth;
     }
     let eap;
     if (security == mojom.SecurityType.kWpaEap) {
@@ -907,13 +899,10 @@
    */
   getEap_: function(properties, opt_create) {
     let eap;
-    switch (properties.type) {
-      case mojom.NetworkType.kWiFi:
-        eap = properties.wifi.eap;
-        break;
-      case mojom.NetworkType.kEthernet:
-        eap = properties.ethernet.eap;
-        break;
+    if (properties.typeConfig.wifi) {
+      eap = properties.typeConfig.wifi.eap;
+    } else if (properties.typeConfig.ethernet) {
+      eap = properties.typeConfig.ethernet.eap;
     }
     if (opt_create) {
       return eap || {
@@ -931,10 +920,10 @@
   setEap_: function(eapProperties) {
     switch (this.mojoType_) {
       case mojom.NetworkType.kWiFi:
-        this.configProperties_.wifi.eap = eapProperties;
+        this.configProperties_.typeConfig.wifi.eap = eapProperties;
         break;
       case mojom.NetworkType.kEthernet:
-        this.configProperties_.ethernet.eap = eapProperties;
+        this.configProperties_.typeConfig.ethernet.eap = eapProperties;
         break;
     }
     this.set('eapProperties_', eapProperties);
@@ -964,7 +953,7 @@
    * @private
    */
   getVpnTypeFromProperties_: function(properties) {
-    const vpn = properties.vpn;
+    const vpn = properties.typeConfig.vpn;
     assert(vpn);
     if (vpn.type == mojom.VpnType.kL2TPIPsec) {
       return vpn.ipSec.authenticationType == 'Cert' ?
@@ -980,7 +969,7 @@
       return;
     }
 
-    const vpn = this.configProperties_.vpn;
+    const vpn = this.configProperties_.typeConfig.vpn;
     if (!vpn) {
       this.showVpn_ = null;
       this.updateCertError_();
@@ -1039,7 +1028,7 @@
     if (this.vpnType_ != VPNConfigType.L2TP_IPSEC_CERT) {
       return;
     }
-    const ipSec = this.configProperties_.vpn.ipSec;
+    const ipSec = this.configProperties_.typeConfig.vpn.ipSec;
     const pem = ipSec.serverCaPems ? ipSec.serverCaPems[0] : undefined;
     const certId =
         ipSec.clientCertType == 'PKCS11Id' ? ipSec.clientCertPkcs11Id : '';
@@ -1051,7 +1040,7 @@
     if (this.vpnType_ != VPNConfigType.OPEN_VPN) {
       return;
     }
-    const openVpn = this.configProperties_.vpn.openVpn;
+    const openVpn = this.configProperties_.typeConfig.vpn.openVpn;
     const pem = openVpn.serverCaPems ? openVpn.serverCaPems[0] : undefined;
     const certId =
         openVpn.clientCertType == 'PKCS11Id' ? openVpn.clientCertPkcs11Id : '';
@@ -1210,11 +1199,11 @@
     }
 
     if (type == mojom.NetworkType.kWiFi) {
-      if (!this.configProperties_.wifi.ssid) {
+      if (!this.configProperties_.typeConfig.wifi.ssid) {
         return false;
       }
       if (this.configRequiresPassphrase_(type, this.securityType)) {
-        const passphrase = this.configProperties_.wifi.passphrase;
+        const passphrase = this.configProperties_.typeConfig.wifi.passphrase;
         if (!passphrase || passphrase.length < this.MIN_PASSPHRASE_LENGTH) {
           return false;
         }
@@ -1390,7 +1379,7 @@
    * @private
    */
   vpnIsConfigured_: function() {
-    const vpn = this.configProperties_.vpn;
+    const vpn = this.configProperties_.typeConfig.vpn;
     if (!this.configProperties_.name || !vpn || !vpn.host) {
       return false;
     }
@@ -1425,16 +1414,17 @@
       this.setEapProperties_(eap);
     }
     if (this.mojoType_ == mojom.NetworkType.kVPN) {
+      const vpnConfig = propertiesToSet.typeConfig.vpn;
       // VPN.Host can be an IP address but will not be recognized as such if
       // there is initial whitespace, so trim it.
-      if (propertiesToSet.vpn.host != undefined) {
-        propertiesToSet.vpn.host = propertiesToSet.vpn.host.trim();
+      if (vpnConfig.host != undefined) {
+        vpnConfig.host = vpnConfig.host.trim();
       }
-      if (propertiesToSet.vpn.type == mojom.VpnType.kOpenVPN) {
+      if (vpnConfig.type == mojom.VpnType.kOpenVPN) {
         this.setOpenVPNProperties_(propertiesToSet);
         delete propertiesToSet.ipSec;
         delete propertiesToSet.l2tp;
-      } else if (propertiesToSet.vpn.type == mojom.VpnType.kL2TPIPsec) {
+      } else if (vpnConfig.type == mojom.VpnType.kL2TPIPsec) {
         this.setVpnIPsecProperties_(propertiesToSet);
         delete propertiesToSet.openVpn;
       }
@@ -1488,7 +1478,7 @@
    * @private
    */
   setOpenVPNProperties_: function(propertiesToSet) {
-    const openVpn = propertiesToSet.vpn.openVpn;
+    const openVpn = propertiesToSet.typeConfig.vpn.openVpn;
     assert(!!openVpn);
 
     openVpn.serverCaPems = this.getServerCaPems_();
@@ -1507,7 +1497,7 @@
     }
 
     openVpn.saveCredentials = this.vpnSaveCredentials_;
-    propertiesToSet.vpn.openVpn = openVpn;
+    propertiesToSet.typeConfig.vpn.openVpn = openVpn;
   },
 
   /**
@@ -1515,7 +1505,7 @@
    * @private
    */
   setVpnIPsecProperties_: function(propertiesToSet) {
-    const vpn = propertiesToSet.vpn;
+    const vpn = propertiesToSet.typeConfig.vpn;
     assert(vpn.ipSec);
     assert(vpn.l2tp);
 
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
index 75121e8..3099dcd 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html
@@ -190,7 +190,7 @@
       }
     </style>
 
-    <div id="root" on-contextmenu="onContextMenu_" on-tap="onRootTap_">
+    <div id="root" on-tap="onRootTap_">
       <div id="pinInputDiv">
         <cr-input id="pinInput" type="password" value="{{value}}"
             is-input-rtl$="[[isInputRtl_(value)]]" aria-label="[[ariaLabel]]"
@@ -278,6 +278,7 @@
               on-pointerdown="onBackspacePointerDown_"
               on-pointerout="clearAndReset_"
               on-pointerup="onBackspacePointerUp_"
+              on-contextmenu="onBackspaceContextMenu_"
               title="[[i18n('pinKeyboardDeleteAccessibleName')]]">
           </cr-icon-button>
         </div>
diff --git a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
index 7f9ad440..fdb066a 100644
--- a/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
+++ b/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js
@@ -492,12 +492,20 @@
   },
 
   /**
-   * Catch and stop propagation of context menu events since we the backspace
-   * button can be held down on touch.
-   * @param {!Event} e
+   * @param {!MouseEvent} e
    * @private
    */
-  onContextMenu_: function(e) {
+  onBackspaceContextMenu_: function(e) {
+    // Note: If e.which is 0, this represents "no button" (i.e., a long-press).
+    // If this event was triggered by another value (e.g., right click - 3),
+    // return early and allow the context menu to be shown.
+    if (e.which) {
+      return;
+    }
+
+    // If the user was long-pressing the backspace button, that user likely was
+    // trying to remove several numbers from the PIN text field rapidly, so
+    // don't show the context menu.
     e.preventDefault();
     e.stopPropagation();
   },
diff --git a/ui/webui/resources/js/chromeos/onc_mojo.js b/ui/webui/resources/js/chromeos/onc_mojo.js
index 860fade5..bc8dc27 100644
--- a/ui/webui/resources/js/chromeos/onc_mojo.js
+++ b/ui/webui/resources/js/chromeos/onc_mojo.js
@@ -663,7 +663,7 @@
    * @param {!chromeos.networkConfig.mojom.NetworkType} type
    * @param {string} guid
    * @param {string} name
-   * @return {chromeos.networkConfig.mojom.ManagedProperties}
+   * @return {!chromeos.networkConfig.mojom.ManagedProperties}
    */
   static getDefaultManagedProperties(type, guid, name) {
     const mojom = chromeos.networkConfig.mojom;
@@ -729,6 +729,34 @@
   }
 
   /**
+   * Returns a ConfigProperties object with a default networkType struct
+   * based on |type|.
+   * @param {!chromeos.networkConfig.mojom.NetworkType} type
+   * @return {!chromeos.networkConfig.mojom.ConfigProperties}
+   */
+  static getDefaultConfigProperties(type) {
+    const mojom = chromeos.networkConfig.mojom;
+    switch (type) {
+      case mojom.NetworkType.kCellular:
+        return {typeConfig: {cellular: {}}};
+        break;
+      case mojom.NetworkType.kEthernet:
+        return {typeConfig: {ethernet: {authentication: 'None'}}};
+        break;
+      case mojom.NetworkType.kVPN:
+        return {typeConfig: {vpn: {type: mojom.VpnType.kOpenVPN}}};
+        break;
+      case mojom.NetworkType.kWiFi:
+        return {
+          typeConfig: {wifi: {ssid: '', security: mojom.SecurityType.kNone}}
+        };
+        break;
+    }
+    assertNotReached('Unexpected type: ' + type.toString());
+    return {typeConfig: {}};
+  }
+
+  /**
    * Sets the value of a property in an mojo config dictionary.
    * @param {!chromeos.networkConfig.mojom.ConfigProperties} config
    * @param {string} key The property key which may be nested, e.g. 'foo.bar'
@@ -931,7 +959,7 @@
     }
 
     // Set ONC IP config properties to existing values + new values.
-    const config = {type: managedProperties.type};
+    const config = OncMojo.getDefaultConfigProperties(managedProperties.type);
     config.ipAddressConfigType = ipConfigType;
     config.nameServersConfigType = nsConfigType;
     if (ipConfigType == 'Static') {
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index 48dbf1d8..a4e387c5 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -9,12 +9,14 @@
   java_files = [
     "org/chromium/weblayer_private/BrowserControllerImpl.java",
     "org/chromium/weblayer_private/BrowserFragmentControllerImpl.java",
+    "org/chromium/weblayer_private/BrowserFragmentImpl.java",
     "org/chromium/weblayer_private/BrowserObserverProxy.java",
     "org/chromium/weblayer_private/ContentView.java",
     "org/chromium/weblayer_private/ContentViewRenderView.java",
     "org/chromium/weblayer_private/NavigationControllerImpl.java",
     "org/chromium/weblayer_private/NavigationImpl.java",
     "org/chromium/weblayer_private/ProfileImpl.java",
+    "org/chromium/weblayer_private/ProfileManager.java",
     "org/chromium/weblayer_private/TopControlsContainerView.java",
     "org/chromium/weblayer_private/WebLayerImpl.java",
     "org/chromium/weblayer_private/ChildProcessServiceImpl.java",
@@ -48,6 +50,7 @@
     "org/chromium/weblayer_private/aidl/ObjectWrapper.java",
     "org/chromium/weblayer_private/aidl/WebLayerVersion.java",
     "org/chromium/weblayer_private/aidl/APICallException.java",
+    "org/chromium/weblayer_private/aidl/BrowserFragmentArgs.java",
   ]
 
   srcjar_deps = [ ":aidl" ]
@@ -58,6 +61,7 @@
   sources = [
     "org/chromium/weblayer_private/aidl/IBrowserController.aidl",
     "org/chromium/weblayer_private/aidl/IBrowserControllerClient.aidl",
+    "org/chromium/weblayer_private/aidl/IBrowserFragment.aidl",
     "org/chromium/weblayer_private/aidl/IBrowserFragmentController.aidl",
     "org/chromium/weblayer_private/aidl/IChildProcessService.aidl",
     "org/chromium/weblayer_private/aidl/IClientNavigation.aidl",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
index 184ae25..e7d3fc2 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserControllerImpl.java
@@ -130,8 +130,8 @@
     public void destroy() {
         BrowserControllerImplJni.get().setTopControlsContainerView(
                 mNativeBrowserController, BrowserControllerImpl.this, 0);
-        mContentViewRenderView.destroy();
         mTopControlsContainerView.destroy();
+        mContentViewRenderView.destroy();
         if (mBrowserObserverProxy != null) mBrowserObserverProxy.destroy();
         mBrowserObserverProxy = null;
         mNavigationController = null;
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentControllerImpl.java
index 89aeba9..48f6242 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentControllerImpl.java
@@ -4,60 +4,62 @@
 
 package org.chromium.weblayer_private;
 
+import android.content.Context;
+import android.os.Bundle;
 import android.view.View;
 
-import org.chromium.weblayer_private.aidl.IBrowserController;
 import org.chromium.weblayer_private.aidl.IBrowserFragmentController;
 import org.chromium.weblayer_private.aidl.IObjectWrapper;
-import org.chromium.weblayer_private.aidl.IRemoteFragment;
-import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
+import org.chromium.weblayer_private.aidl.IProfile;
 
 /**
  * Implementation of {@link IBrowserFragmentController}.
  */
 public class BrowserFragmentControllerImpl extends IBrowserFragmentController.Stub {
-    private final BrowserControllerImpl mController;
-    private final BrowserRemoteFragmentImpl mRemoteFragmentImpl;
+    private final ProfileImpl mProfile;
+    private BrowserControllerImpl mTabController;
 
-    public BrowserFragmentControllerImpl(BrowserControllerImpl controller,
-            IRemoteFragmentClient fragmentClient) {
-        mController = controller;
-        mRemoteFragmentImpl = new BrowserRemoteFragmentImpl(fragmentClient);
+    public BrowserFragmentControllerImpl(ProfileImpl profile, Bundle savedInstanceState) {
+        mProfile = profile;
+        // Restore tabs etc from savedInstanceState here.
+    }
+
+    public void onFragmentAttached(Context context) {
+        mTabController = new BrowserControllerImpl(context, mProfile);
+    }
+
+    public void onFragmentDetached() {
+        mTabController.destroy();
+        mTabController = null;
     }
 
     @Override
     public void setTopView(IObjectWrapper view) {
-        mController.setTopView(view);
+        getBrowserController().setTopView(view);
     }
 
     @Override
     public void setSupportsEmbedding(boolean enable, IObjectWrapper valueCallback) {
-        mController.setSupportsEmbedding(enable, valueCallback);
+        getBrowserController().setSupportsEmbedding(enable, valueCallback);
     }
 
     @Override
-    public IRemoteFragment getRemoteFragment() {
-        return mRemoteFragmentImpl;
-    }
-
-    @Override
-    public IBrowserController getBrowserController() {
-        return mController;
-    }
-
-    @Override
-    public void destroy() {
-        mController.destroy();
-    }
-
-    private class BrowserRemoteFragmentImpl extends RemoteFragmentImpl {
-        public BrowserRemoteFragmentImpl(IRemoteFragmentClient client) {
-            super(client);
+    public BrowserControllerImpl getBrowserController() {
+        if (mTabController == null) {
+            throw new RuntimeException("Currently BrowserController requires Activity context, so "
+                    + "it exists only while BrowserFragment is attached to an Activity");
         }
-
-        @Override
-        public View onCreateView() {
-            return mController.getView();
-        }
+        return mTabController;
     }
+
+    @Override
+    public IProfile getProfile() {
+        return mProfile;
+    }
+
+    public View getFragmentView() {
+        return mTabController.getView();
+    }
+
+    public void destroy() {}
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
new file mode 100644
index 0000000..1476def
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/BrowserFragmentImpl.java
@@ -0,0 +1,88 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.View;
+
+import org.chromium.weblayer_private.aidl.BrowserFragmentArgs;
+import org.chromium.weblayer_private.aidl.IBrowserFragment;
+import org.chromium.weblayer_private.aidl.IBrowserFragmentController;
+import org.chromium.weblayer_private.aidl.IRemoteFragment;
+import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
+
+public class BrowserFragmentImpl extends RemoteFragmentImpl {
+
+    private final ProfileImpl mProfile;
+
+    private BrowserFragmentControllerImpl mController;
+    private Context mContext;
+
+    public BrowserFragmentImpl(ProfileManager profileManager, IRemoteFragmentClient client,
+            Bundle fragmentArgs) {
+        super(client);
+        mProfile = profileManager.getProfile(fragmentArgs.getString(
+                BrowserFragmentArgs.PROFILE_PATH));
+    }
+
+    @Override
+    public void onAttach(Context context) {
+        super.onAttach(context);
+        mContext = context;
+        if (mController != null) { // On first creation, onAttach is called before onCreate
+            mController.onFragmentAttached(context);
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mController = new BrowserFragmentControllerImpl(mProfile, savedInstanceState);
+        if (mContext != null) {
+            mController.onFragmentAttached(mContext);
+        }
+    }
+
+    @Override
+    public View onCreateView() {
+        return mController.getFragmentView();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mController.destroy();
+        mController = null;
+    }
+
+    @Override
+    public void onDetach() {
+        super.onDetach();
+        // mController != null if fragment is retained, otherwise onDestroy is called first.
+        if (mController != null) {
+            mController.onFragmentDetached();
+        }
+        mContext = null;
+    }
+
+    public IBrowserFragment asIBrowserFragment() {
+        return new IBrowserFragment.Stub() {
+            @Override
+            public IRemoteFragment asRemoteFragment() {
+                return BrowserFragmentImpl.this;
+            }
+
+            @Override
+            public IBrowserFragmentController getController() {
+                if (mController == null) {
+                    throw new RuntimeException("BrowserFragmentController is available only between"
+                            + " BrowserFragment's onCreate() and onDestroy().");
+                }
+                return mController;
+            }
+        };
+    }
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
index c0e4ad5..8a58b5a9 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ProfileImpl.java
@@ -4,28 +4,26 @@
 
 package org.chromium.weblayer_private;
 
-import android.content.Context;
-
 import org.chromium.base.annotations.JNINamespace;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.weblayer_private.aidl.IBrowserFragmentController;
-import org.chromium.weblayer_private.aidl.IObjectWrapper;
 import org.chromium.weblayer_private.aidl.IProfile;
-import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
-import org.chromium.weblayer_private.aidl.ObjectWrapper;
 
 @JNINamespace("weblayer")
 public final class ProfileImpl extends IProfile.Stub {
     private long mNativeProfile;
+    private Runnable mOnDestroyCallback;
 
-    public ProfileImpl(String path) {
+    ProfileImpl(String path, Runnable onDestroyCallback) {
         mNativeProfile = ProfileImplJni.get().createProfile(path);
+        mOnDestroyCallback = onDestroyCallback;
     }
 
     @Override
     public void destroy() {
         ProfileImplJni.get().deleteProfile(mNativeProfile);
         mNativeProfile = 0;
+        mOnDestroyCallback.run();
+        mOnDestroyCallback = null;
     }
 
     @Override
@@ -33,15 +31,6 @@
         ProfileImplJni.get().clearBrowsingData(mNativeProfile);
     }
 
-    @Override
-    public IBrowserFragmentController createBrowserFragmentController(IRemoteFragmentClient
-            fragmentClient,
-            IObjectWrapper context) {
-        BrowserControllerImpl browserController = new BrowserControllerImpl(
-                    ObjectWrapper.unwrap(context, Context.class), this);
-        return new BrowserFragmentControllerImpl(browserController, fragmentClient);
-    }
-
     long getNativeProfile() {
         return mNativeProfile;
     }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/ProfileManager.java b/weblayer/browser/java/org/chromium/weblayer_private/ProfileManager.java
new file mode 100644
index 0000000..7965846
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/ProfileManager.java
@@ -0,0 +1,28 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Creates and maintains the active Profiles.
+ */
+public class ProfileManager {
+    private final Map<String, ProfileImpl> mProfiles = new HashMap<>();
+
+    /** Returns existing or new Profile associated with the given path */
+    public ProfileImpl getProfile(String path) {
+        if (path == null) throw new IllegalArgumentException("Path shouldn't be null");
+        ProfileImpl existingProfile = mProfiles.get(path);
+        if (existingProfile != null) {
+            return existingProfile;
+        }
+
+        ProfileImpl profile = new ProfileImpl(path, () -> mProfiles.remove(path));
+        mProfiles.put(path, profile);
+        return profile;
+    }
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java b/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
index 8b246f5ee..d0e5b17 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/TopControlsContainerView.java
@@ -52,6 +52,7 @@
 
     // Used to  delay updating the image for the layer.
     private final Runnable mRefreshResourceIdRunnable = () -> {
+        if (mView == null) return;
         TopControlsContainerViewJni.get().updateTopControlsResource(
                 mNativeTopControlsContainerView, TopControlsContainerView.this);
     };
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
index 53b8ab2..2a3f8e2 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/WebLayerImpl.java
@@ -5,6 +5,7 @@
 package org.chromium.weblayer_private;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.webkit.ValueCallback;
 
@@ -18,8 +19,9 @@
 import org.chromium.content_public.browser.ChildProcessCreationParams;
 import org.chromium.content_public.browser.DeviceUtils;
 import org.chromium.ui.base.ResourceBundle;
+import org.chromium.weblayer_private.aidl.IBrowserFragment;
 import org.chromium.weblayer_private.aidl.IObjectWrapper;
-import org.chromium.weblayer_private.aidl.IProfile;
+import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
 import org.chromium.weblayer_private.aidl.IWebLayer;
 import org.chromium.weblayer_private.aidl.ObjectWrapper;
 import org.chromium.weblayer_private.aidl.WebLayerVersion;
@@ -32,6 +34,8 @@
     // TODO: Configure this from the client.
     private static final String COMMAND_LINE_FILE = "/data/local/tmp/weblayer-command-line";
 
+    private final ProfileManager mProfileManager = new ProfileManager();
+
     @UsedByReflection("WebLayer")
     public static IBinder create() {
         return new WebLayerImpl();
@@ -48,11 +52,6 @@
     }
 
     @Override
-    public IProfile createProfile(String path) {
-        return new ProfileImpl(path);
-    }
-
-    @Override
     public void initAndLoadAsync(
             IObjectWrapper webLayerContextWrapper, IObjectWrapper loadedCallbackWrapper) {
         Context context = ObjectWrapper.unwrap(webLayerContextWrapper, Context.class);
@@ -97,4 +96,13 @@
                 .startBrowserProcessesSync(
                         /* singleProcess*/ false);
     }
+
+    @Override
+    public IBrowserFragment createBrowserFragmentImpl(IRemoteFragmentClient fragmentClient,
+            IObjectWrapper fragmentArgs) {
+        Bundle unwrappedArgs = ObjectWrapper.unwrap(fragmentArgs, Bundle.class);
+        BrowserFragmentImpl fragment = new BrowserFragmentImpl(mProfileManager, fragmentClient,
+                unwrappedArgs);
+        return fragment.asIBrowserFragment();
+    }
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/BrowserFragmentArgs.java b/weblayer/browser/java/org/chromium/weblayer_private/aidl/BrowserFragmentArgs.java
new file mode 100644
index 0000000..e5815d67
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/BrowserFragmentArgs.java
@@ -0,0 +1,10 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.aidl;
+
+/** Keys for the Bundle of arguments with which BrowserFragments are created. */
+public interface BrowserFragmentArgs {
+    String PROFILE_PATH = "profile_path";
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserFragment.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserFragment.aidl
new file mode 100644
index 0000000..a83f15c
--- /dev/null
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserFragment.aidl
@@ -0,0 +1,13 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer_private.aidl;
+
+import org.chromium.weblayer_private.aidl.IBrowserFragmentController;
+import org.chromium.weblayer_private.aidl.IRemoteFragment;
+
+interface IBrowserFragment {
+  IRemoteFragment asRemoteFragment() = 0;
+  IBrowserFragmentController getController() = 1;
+}
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserFragmentController.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserFragmentController.aidl
index db474ae..bd4b6ee5 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserFragmentController.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IBrowserFragmentController.aidl
@@ -6,12 +6,10 @@
 
 import org.chromium.weblayer_private.aidl.IBrowserController;
 import org.chromium.weblayer_private.aidl.IObjectWrapper;
-import org.chromium.weblayer_private.aidl.IRemoteFragment;
 
 interface IBrowserFragmentController {
-  void destroy() = 0;
+  IProfile getProfile() = 0;
   IBrowserController getBrowserController() = 1;
-  IRemoteFragment getRemoteFragment() = 2;
   void setTopView(IObjectWrapper view) = 3;
 
   // |valueCallback| is a wrapped ValueCallback<Boolean> instead. The bool value in |valueCallback|
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
index 98d7c21a..5a034ba 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IProfile.aidl
@@ -4,21 +4,8 @@
 
 package org.chromium.weblayer_private.aidl;
 
-import org.chromium.weblayer_private.aidl.IBrowserFragmentController;
-import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
-import org.chromium.weblayer_private.aidl.IObjectWrapper;
-
 interface IProfile {
   void destroy() = 0;
 
   void clearBrowsingData() = 1;
-
-  /**
-   * Creates a new IBrowserFragmentController.
-   * @param fragmentClient IRemoteFragmentClient that will host the Fragment implemented on the
-   * weblayer side.
-   * @param context Context that refers the the weblayer implementation
-   */
-  IBrowserFragmentController createBrowserFragmentController(
-          in IRemoteFragmentClient fragmentClient, in IObjectWrapper context) = 2;
 }
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl
index 1da97ad2..b89bd54 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl
+++ b/weblayer/browser/java/org/chromium/weblayer_private/aidl/IWebLayer.aidl
@@ -4,12 +4,13 @@
 
 package org.chromium.weblayer_private.aidl;
 
+import android.os.Bundle;
+
 import org.chromium.weblayer_private.aidl.IObjectWrapper;
-import org.chromium.weblayer_private.aidl.IProfile;
+import org.chromium.weblayer_private.aidl.IBrowserFragment;
+import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
 
 interface IWebLayer {
-  IProfile createProfile(in String path) = 0;
-
   // Initializes WebLayer and starts loading. It is expected that is called
   // before anything else. |loadedCallback| is a ValueCallback that is called
   // when load completes. |webLayerContext| is a Context that refers to the
@@ -19,4 +20,13 @@
 
   // Blocks until loading has completed.
   void loadSync() = 2;
+
+  // Creates the WebLayer counterpart to a BrowserFragment - a BrowserFragmentImpl
+  //
+  // @param fragmentClient Representative of the Fragment on the client side through which
+  // WebLayer can call methods on Fragment.
+  // @param fragmentArgs Bundle of arguments with which the Fragment was created on the client side
+  // (see Fragment#setArguments).
+  IBrowserFragment createBrowserFragmentImpl(in IRemoteFragmentClient fragmentClient,
+      in IObjectWrapper fragmentArgs) = 3;
 }
diff --git a/weblayer/public/java/BUILD.gn b/weblayer/public/java/BUILD.gn
index f9fdcd58..070cc6bf 100644
--- a/weblayer/public/java/BUILD.gn
+++ b/weblayer/public/java/BUILD.gn
@@ -34,6 +34,7 @@
       "org/chromium/weblayer/NavigationObserver.java",
       "org/chromium/weblayer/ObserverList.java",
       "org/chromium/weblayer/Profile.java",
+      "org/chromium/weblayer/ProfileManager.java",
       "org/chromium/weblayer/WebLayer.java",
       "org/chromium/weblayer/ChildProcessService.java",
       "org/chromium/weblayer/UnsupportedVersionException.java",
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java b/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java
index 4c199fd..05db0e2 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserFragment.java
@@ -13,13 +13,27 @@
 import android.view.ViewGroup;
 
 import org.chromium.weblayer_private.aidl.APICallException;
+import org.chromium.weblayer_private.aidl.IBrowserFragment;
 import org.chromium.weblayer_private.aidl.IObjectWrapper;
-import org.chromium.weblayer_private.aidl.IRemoteFragment;
 import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
 import org.chromium.weblayer_private.aidl.ObjectWrapper;
 
+import java.util.concurrent.Future;
+
 /**
  * WebLayer's fragment implementation.
+ *
+ * All the browser APIs, such as loading pages can be accessed via
+ * {@link BrowserFragmentController}, which can be retrieved with {@link #getController} after
+ * the fragment received onCreate the call.
+ *
+ * Attaching a BrowserFragment to an Activity requires WebLayer to be initialized, so
+ * BrowserFragment will block the thread in onAttach until it's done. To prevent this,
+ * asynchronously "pre-warm" WebLayer using {@link WebLayer#create} prior to using BrowserFragments.
+ *
+ * Unfortunately, when the system restores the BrowserFragment after killing the process, it
+ * attaches the fragment immediately on activity's onCreate event, so there is currently no way to
+ * asynchronously init WebLayer in that case.
  */
 public final class BrowserFragment extends Fragment {
     private final IRemoteFragmentClient mClientImpl = new IRemoteFragmentClient.Stub() {
@@ -84,42 +98,83 @@
         }
     };
 
-    private IRemoteFragment mRemoteFragment;
+    // Nonnull after first onAttach().
+    private IBrowserFragment mImpl;
+    private WebLayer mWebLayer;
 
-    // TODO(pshmakov): how do we deal with FragmentManager restoring this Fragment on its own?
-    /* package */ void setRemoteFragment(IRemoteFragment remoteFragment) {
-        mRemoteFragment = remoteFragment;
+    // Nonnull between onCreate() and onDestroy().
+    private BrowserFragmentController mBrowserFragmentController;
+
+    /**
+     * This constructor is for the system FragmentManager only. Please use
+     * {@link WebLayer#createBrowserFragment}.
+     */
+    public BrowserFragment() {
+        super();
     }
 
-    /* package */ IRemoteFragmentClient asIRemoteFragmentClient() {
-        return mClientImpl;
-    }
-
-    @Override
-    public View onCreateView(
-            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
-        try {
-            return ObjectWrapper.unwrap(mRemoteFragment.handleOnCreateView(), View.class);
-        } catch (RemoteException e) {
-            throw new APICallException(e);
+    /**
+     * Returns the {@link BrowserFragmentController} associated with this fragment.
+     * The controller is available only between BrowserFragment's onCreate() and onDestroy().
+     */
+    public BrowserFragmentController getController() {
+        if (mBrowserFragmentController == null) {
+            throw new RuntimeException("BrowserFragmentController is available only between "
+                    + "BrowserFragment's onCreate() and onDestroy().");
         }
+        return mBrowserFragmentController;
     }
 
     @SuppressWarnings("MissingSuperCall")
     @Override
     public void onAttach(Context context) {
+        // This is the first lifecycle event and also the first time we can get app context (unless
+        // the embedder has already called getController). So it's the latest and at the same time
+        // the earliest moment when we can initialize WebLayer without missing any lifecycle events.
+        ensureConnectedToWebLayer(context.getApplicationContext());
         try {
-            mRemoteFragment.handleOnAttach(ObjectWrapper.wrap(context));
+            mImpl.asRemoteFragment().handleOnAttach(ObjectWrapper.wrap(context));
+            // handleOnAttach results in creating BrowserFragmentControllerImpl on the other side.
+            // Now we retrieve it, and build BrowserFragmentController on this side.
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
     }
 
+    private void ensureConnectedToWebLayer(Context appContext) {
+        if (mImpl != null) {
+            return; // Already initialized.
+        }
+        Bundle args = getArguments();
+        if (args == null) {
+            throw new RuntimeException("BrowserFragment was created without arguments.");
+        }
+        try {
+            Future<WebLayer> future = WebLayer.create(appContext);
+            mWebLayer = future.get();
+            mImpl = mWebLayer.connectFragment(mClientImpl, args);
+        } catch (Exception e) {
+            throw new RuntimeException("Failed to initialize WebLayer", e);
+        }
+    }
+
     @SuppressWarnings("MissingSuperCall")
     @Override
     public void onCreate(Bundle savedInstanceState) {
         try {
-            mRemoteFragment.handleOnCreate(ObjectWrapper.wrap(savedInstanceState));
+            mImpl.asRemoteFragment().handleOnCreate(ObjectWrapper.wrap(savedInstanceState));
+            mBrowserFragmentController = new BrowserFragmentController(mImpl.getController(),
+                    mWebLayer.getProfileManager());
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        try {
+            return ObjectWrapper.unwrap(mImpl.asRemoteFragment().handleOnCreateView(), View.class);
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -129,7 +184,8 @@
     @Override
     public void onActivityCreated(Bundle savedInstanceState) {
         try {
-            mRemoteFragment.handleOnActivityCreated(ObjectWrapper.wrap(savedInstanceState));
+            mImpl.asRemoteFragment().handleOnActivityCreated(
+                    ObjectWrapper.wrap(savedInstanceState));
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -139,7 +195,7 @@
     @Override
     public void onStart() {
         try {
-            mRemoteFragment.handleOnStart();
+            mImpl.asRemoteFragment().handleOnStart();
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -149,7 +205,7 @@
     @Override
     public void onResume() {
         try {
-            mRemoteFragment.handleOnResume();
+            mImpl.asRemoteFragment().handleOnResume();
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -159,7 +215,7 @@
     @Override
     public void onSaveInstanceState(Bundle outState) {
         try {
-            mRemoteFragment.handleOnSaveInstanceState(ObjectWrapper.wrap(outState));
+            mImpl.asRemoteFragment().handleOnSaveInstanceState(ObjectWrapper.wrap(outState));
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -169,7 +225,7 @@
     @Override
     public void onPause() {
         try {
-            mRemoteFragment.handleOnPause();
+            mImpl.asRemoteFragment().handleOnPause();
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -179,7 +235,7 @@
     @Override
     public void onStop() {
         try {
-            mRemoteFragment.handleOnStop();
+            mImpl.asRemoteFragment().handleOnStop();
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -189,7 +245,7 @@
     @Override
     public void onDestroyView() {
         try {
-            mRemoteFragment.handleOnDestroyView();
+            mImpl.asRemoteFragment().handleOnDestroyView();
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
@@ -199,17 +255,19 @@
     @Override
     public void onDestroy() {
         try {
-            mRemoteFragment.handleOnDestroy();
+            mImpl.asRemoteFragment().handleOnDestroy();
+            // The other side does the clean up automatically in handleOnDestroy()
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
+        mBrowserFragmentController = null;
     }
 
     @SuppressWarnings("MissingSuperCall")
     @Override
     public void onDetach() {
         try {
-            mRemoteFragment.handleOnDetach();
+            mImpl.asRemoteFragment().handleOnDetach();
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
diff --git a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentController.java b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentController.java
index 3d928b3..95464fd8 100644
--- a/weblayer/public/java/org/chromium/weblayer/BrowserFragmentController.java
+++ b/weblayer/public/java/org/chromium/weblayer/BrowserFragmentController.java
@@ -5,7 +5,6 @@
 package org.chromium.weblayer;
 
 import android.os.RemoteException;
-import android.support.v4.app.Fragment;
 import android.view.View;
 import android.webkit.ValueCallback;
 
@@ -18,20 +17,13 @@
  */
 public final class BrowserFragmentController {
     private final IBrowserFragmentController mImpl;
-    private final BrowserFragment mFragment;
+    private final ProfileManager mProfileManager;
     private BrowserController mController;
 
-    BrowserFragmentController(IBrowserFragmentController impl, BrowserFragment fragment) {
-        mImpl = impl;
-        mFragment = fragment;
-    }
 
-    public void destroy() {
-        try {
-            mImpl.destroy();
-        } catch (RemoteException e) {
-            throw new APICallException(e);
-        }
+    BrowserFragmentController(IBrowserFragmentController impl, ProfileManager profileManager) {
+        mImpl = impl;
+        mProfileManager = profileManager;
     }
 
     // TODO(pshmakov): rename this to BrowserTabController.
@@ -46,10 +38,6 @@
         return mController;
     }
 
-    public Fragment getFragment() {
-        return mFragment;
-    }
-
     public void setTopView(View view) {
         try {
             mImpl.setTopView(ObjectWrapper.wrap(view));
@@ -82,4 +70,16 @@
             throw new APICallException(e);
         }
     }
+
+    /**
+     * Returns {@link Profile} associated with this Browser Fragment. Multiple fragments can share
+     * the same Profile.
+     */
+    public Profile getProfile() {
+        try {
+            return mProfileManager.getProfileFor(mImpl.getProfile());
+        } catch (RemoteException e) {
+            throw new APICallException(e);
+        }
+    }
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/Profile.java b/weblayer/public/java/org/chromium/weblayer/Profile.java
index 93b6e14..4ef1fb3 100644
--- a/weblayer/public/java/org/chromium/weblayer/Profile.java
+++ b/weblayer/public/java/org/chromium/weblayer/Profile.java
@@ -4,13 +4,10 @@
 
 package org.chromium.weblayer;
 
-import android.content.Context;
 import android.os.RemoteException;
 
 import org.chromium.weblayer_private.aidl.APICallException;
-import org.chromium.weblayer_private.aidl.IBrowserFragmentController;
 import org.chromium.weblayer_private.aidl.IProfile;
-import org.chromium.weblayer_private.aidl.ObjectWrapper;
 
 /**
  * Profile holds state (typically on disk) needed for browsing. Create a
@@ -18,9 +15,12 @@
  */
 public final class Profile {
     private IProfile mImpl;
+    private Runnable mOnDestroyRunnable;
 
-    Profile(IProfile impl) {
+
+    /* package */ Profile(IProfile impl, Runnable onDestroyRunnable) {
         mImpl = impl;
+        mOnDestroyRunnable = onDestroyRunnable;
     }
 
     @Override
@@ -28,14 +28,6 @@
         // TODO(sky): figure out right assertion here if mImpl is non-null.
     }
 
-    public void destroy() {
-        try {
-            mImpl.destroy();
-        } catch (RemoteException e) {
-            throw new APICallException(e);
-        }
-    }
-
     public void clearBrowsingData() {
         try {
             mImpl.clearBrowsingData();
@@ -44,16 +36,14 @@
         }
     }
 
-    public BrowserFragmentController createBrowserFragmentController(Context context) {
+    public void destroy() {
         try {
-            BrowserFragment fragment = new BrowserFragment();
-            IBrowserFragmentController browserFragmentImpl =
-                    mImpl.createBrowserFragmentController(fragment.asIRemoteFragmentClient(),
-                            ObjectWrapper.wrap(WebLayer.createRemoteContext(context)));
-            fragment.setRemoteFragment(browserFragmentImpl.getRemoteFragment());
-            return new BrowserFragmentController(browserFragmentImpl, fragment);
+            mImpl.destroy();
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
+        mImpl = null;
+        mOnDestroyRunnable.run();
+        mOnDestroyRunnable = null;
     }
 }
diff --git a/weblayer/public/java/org/chromium/weblayer/ProfileManager.java b/weblayer/public/java/org/chromium/weblayer/ProfileManager.java
new file mode 100644
index 0000000..06980c0
--- /dev/null
+++ b/weblayer/public/java/org/chromium/weblayer/ProfileManager.java
@@ -0,0 +1,31 @@
+package org.chromium.weblayer;
+
+import org.chromium.weblayer_private.aidl.IProfile;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/* package */ class ProfileManager {
+
+    private final Map<IProfile, Profile> mProfiles = new HashMap<>();
+
+    /** Returns existing or new Profile associated with the given implementation on WebLayer side */
+    Profile getProfileFor(IProfile impl) {
+        Profile profile = mProfiles.get(impl);
+        if (profile != null) {
+            return profile;
+        }
+
+        profile = new Profile(impl, () -> mProfiles.remove(impl));
+        mProfiles.put(impl, profile);
+        return profile;
+    }
+
+    /** Destroys all the Profiles. */
+    void destroy() {
+        for (Profile profile : mProfiles.values()) {
+            profile.destroy();
+        }
+        mProfiles.clear();
+    }
+}
diff --git a/weblayer/public/java/org/chromium/weblayer/WebLayer.java b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
index fdba0509..a33bb0464 100644
--- a/weblayer/public/java/org/chromium/weblayer/WebLayer.java
+++ b/weblayer/public/java/org/chromium/weblayer/WebLayer.java
@@ -4,7 +4,6 @@
 
 package org.chromium.weblayer;
 
-import android.app.Application;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -21,11 +20,13 @@
 import android.webkit.WebViewFactory;
 
 import org.chromium.weblayer_private.aidl.APICallException;
+import org.chromium.weblayer_private.aidl.BrowserFragmentArgs;
+import org.chromium.weblayer_private.aidl.IBrowserFragment;
+import org.chromium.weblayer_private.aidl.IRemoteFragmentClient;
 import org.chromium.weblayer_private.aidl.IWebLayer;
 import org.chromium.weblayer_private.aidl.ObjectWrapper;
 import org.chromium.weblayer_private.aidl.WebLayerVersion;
 
-import java.io.File;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
 import java.util.concurrent.TimeUnit;
@@ -40,13 +41,14 @@
 
     private static ListenableFuture<WebLayer> sFuture;
 
-    private IWebLayer mImpl;
+    private final IWebLayer mImpl;
+    private final ProfileManager mProfileManager = new ProfileManager();
 
     /**
      * Loads the WebLayer implementation and returns the IWebLayer. This does *not* trigger the
      * implementation to start.
      */
-    private static IWebLayer connectToWebLayerImplementation(Application application)
+    private static IWebLayer connectToWebLayerImplementation(Context application)
             throws UnsupportedVersionException {
         try {
             // TODO: Make asset loading work on L, where WebViewDelegate doesn't exist.
@@ -88,15 +90,16 @@
      * Asynchronously creates and initializes WebLayer. Calling this more than once returns the same
      * object.
      *
-     * @param application The hosting Application
+     * @param appContext The hosting application's Context.
      * @return a ListenableFuture whose value will contain the WebLayer once initialization
      * completes
      */
-    public static ListenableFuture<WebLayer> create(Application application)
+    public static ListenableFuture<WebLayer> create(Context appContext)
             throws UnsupportedVersionException {
         if (sFuture == null) {
-            IWebLayer iWebLayer = connectToWebLayerImplementation(application);
-            sFuture = new WebLayerLoadFuture(iWebLayer, application);
+            IWebLayer iWebLayer = connectToWebLayerImplementation(
+                    appContext.getApplicationContext());
+            sFuture = new WebLayerLoadFuture(iWebLayer, appContext);
         }
         return sFuture;
     }
@@ -107,7 +110,7 @@
     private static final class WebLayerLoadFuture extends ListenableFuture<WebLayer> {
         private final IWebLayer mIWebLayer;
 
-        WebLayerLoadFuture(IWebLayer iWebLayer, Application application) {
+        WebLayerLoadFuture(IWebLayer iWebLayer, Context application) {
             mIWebLayer = iWebLayer;
             ValueCallback<Boolean> loadCallback = new ValueCallback<Boolean>() {
                 @Override
@@ -159,23 +162,39 @@
 
     public void destroy() {
         // TODO: implement me.
+        mProfileManager.destroy();
     }
 
     private WebLayer(IWebLayer iWebLayer) {
         mImpl = iWebLayer;
     }
 
+    public static BrowserFragment createBrowserFragment(String profilePath) {
+        // TODO: use a profile id instead of the path to the actual file.
+        Bundle args = new Bundle();
+        args.putString(BrowserFragmentArgs.PROFILE_PATH, profilePath == null ? "" : profilePath);
+        BrowserFragment fragment = new BrowserFragment();
+        fragment.setArguments(args);
+        return fragment;
+    }
+
     /**
-     * Creates a new Profile with the given path. Pass in an null path for an in-memory profile.
+     * Returns remote counterpart for the BrowserFragment: an {@link IBrowserFragment}.
      */
-    public Profile createProfile(File path) {
+    /* package */ IBrowserFragment connectFragment(
+            IRemoteFragmentClient remoteFragmentClient, Bundle fragmentArgs) {
         try {
-            return new Profile(mImpl.createProfile(path == null ? "" : path.getPath()));
+            return mImpl.createBrowserFragmentImpl(remoteFragmentClient,
+                    ObjectWrapper.wrap(fragmentArgs));
         } catch (RemoteException e) {
             throw new APICallException(e);
         }
     }
 
+    /* package */ ProfileManager getProfileManager() {
+        return mProfileManager;
+    }
+
     /**
      * Creates a Context for the remote (weblayer implementation) side.
      */
diff --git a/weblayer/shell/android/BUILD.gn b/weblayer/shell/android/BUILD.gn
index ef019fd..3a9c326 100644
--- a/weblayer/shell/android/BUILD.gn
+++ b/weblayer/shell/android/BUILD.gn
@@ -255,6 +255,7 @@
     "javatests/src/org/chromium/weblayer/test/SmokeTest.java",
     "javatests/src/org/chromium/weblayer/test/RenderingTest.java",
     "javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java",
+    "javatests/src/org/chromium/weblayer/test/FragmentRestoreTest.java",
     "shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java",
   ]
   additional_apks = [ "//weblayer/shell/android:weblayer_support_apk" ]
diff --git a/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java b/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
index dcb69e2..144022a 100644
--- a/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
+++ b/weblayer/shell/android/browsertests_apk/src/org/chromium/weblayer_browsertests_apk/WebLayerBrowserTestsActivity.java
@@ -18,6 +18,7 @@
 import org.chromium.native_test.NativeBrowserTest;
 import org.chromium.native_test.NativeBrowserTestActivity;
 import org.chromium.weblayer.BrowserController;
+import org.chromium.weblayer.BrowserFragment;
 import org.chromium.weblayer.BrowserFragmentController;
 import org.chromium.weblayer.BrowserObserver;
 import org.chromium.weblayer.ListenableFuture;
@@ -38,13 +39,6 @@
     private View mMainView;
 
     @Override
-    protected void onDestroy() {
-        if (mProfile != null) mProfile.destroy();
-        if (mBrowserFragmentController != null) mBrowserFragmentController.destroy();
-        super.onDestroy();
-    }
-
-    @Override
     protected void initializeBrowserProcess() {
         BrowserStartupController.get(LibraryProcessType.PROCESS_WEBLAYER)
                 .setContentMainCallbackForTests(() -> {
@@ -85,12 +79,12 @@
                 new RelativeLayout.LayoutParams(
                         LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
 
-        mProfile = mWebLayer.createProfile(null);
-
-        mBrowserFragmentController = mProfile.createBrowserFragmentController(this);
+        BrowserFragment fragment = WebLayer.createBrowserFragment(null);
+        mBrowserFragmentController = fragment.getController();
+        mProfile = mBrowserFragmentController.getProfile();
 
         FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.add(viewId, mBrowserFragmentController.getFragment());
+        transaction.add(viewId, fragment);
         transaction.commit();
 
         mBrowserFragmentController.setTopView(topContentsContainer);
diff --git a/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java b/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
index 5ee3fdf..2df0e8b 100644
--- a/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
+++ b/weblayer/shell/android/demo_apk/src/org/chromium/weblayer/demo/WebLayerAnimationDemoActivity.java
@@ -23,8 +23,10 @@
 import android.widget.TextView.OnEditorActionListener;
 
 import org.chromium.weblayer.BrowserController;
+import org.chromium.weblayer.BrowserFragment;
 import org.chromium.weblayer.BrowserFragmentController;
 import org.chromium.weblayer.BrowserObserver;
+import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.Profile;
 import org.chromium.weblayer.WebLayer;
 
@@ -37,11 +39,11 @@
     private static final boolean USE_WEBVIEW = false;
 
     private Profile mProfile;
-    private final BrowserFragmentController mBrowserFragmentControllers[] =
-            new BrowserFragmentController[4];
+    private final BrowserFragment mBrowserFragments[] = new BrowserFragment[4];
     private final ContainerFrameLayout mContainerViews[] = new ContainerFrameLayout[4];
     private final MyWebView mWebViews[] = new MyWebView[4];
     private FrameLayout mMainView;
+    private WebLayer mWebLayer;
 
     public static class ContainerFrameLayout extends FrameLayout {
         private final BrowserFragmentController mFragmentController;
@@ -115,21 +117,22 @@
     }
 
     private void createNewFragment(int index) {
-        mBrowserFragmentControllers[index] = mProfile.createBrowserFragmentController(this);
-        final BrowserController controller = mBrowserFragmentControllers[index]
-                .getBrowserController();
+        int viewId = View.generateViewId();
+
+        mBrowserFragments[index] = WebLayer.createBrowserFragment(null);
+        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+        transaction.add(viewId, mBrowserFragments[index]);
+        transaction.commitNow();
+
+        final BrowserController controller = getFragmentController(index).getBrowserController();
 
         ContainerFrameLayout container =
-                new ContainerFrameLayout(this, mBrowserFragmentControllers[index],
-                        index);
+                new ContainerFrameLayout(this, getFragmentController(index), index);
         mContainerViews[index] = container;
-        int viewId = View.generateViewId();
         container.setId(viewId);
         mMainView.addView(container);
 
-        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.add(viewId, mBrowserFragmentControllers[index].getFragment());
-        transaction.commit();
+
 
         EditText urlView = new EditText(this);
         urlView.setSelectAllOnFocus(true);
@@ -151,7 +154,7 @@
                 return true;
             }
         });
-        mBrowserFragmentControllers[index].setTopView(urlView);
+        getFragmentController(index).setTopView(urlView);
 
         controller.addObserver(new BrowserObserver() {
             @Override
@@ -189,12 +192,16 @@
             }
         } else {
             // Only call init for main process.
-            WebLayer webLayer = null;
             try {
-                webLayer = WebLayer.create(getApplication()).get();
+                mWebLayer = WebLayer.create(getApplication()).get();
             } catch (Exception e) {
                 throw new RuntimeException("failed loading WebLayer", e);
             }
+            if (savedInstanceState != null) {
+                // This prevents FragmentManager from restoring fragments.
+                // TODO(pshmakov): restore fragments properly, as in WebLayerShellActivity.
+                savedInstanceState.remove("android:support:fragments");
+            }
 
             super.onCreate(savedInstanceState);
 
@@ -202,33 +209,18 @@
             mMainView = mainView;
             setContentView(mainView);
 
-            mProfile = webLayer.createProfile(null);
-
             createNewFragment(0);
             createNewFragment(1);
             createNewFragment(2);
+            mProfile = getFragmentController(0).getProfile();
 
-            mBrowserFragmentControllers[0].getBrowserController().getNavigationController()
-                    .navigate(Uri.parse(sanitizeUrl("https://www.google.com")));
-            mBrowserFragmentControllers[1].getBrowserController().getNavigationController()
-                    .navigate(Uri.parse(sanitizeUrl("https://en.wikipedia.org")));
-            mBrowserFragmentControllers[2].getBrowserController().getNavigationController()
-                    .navigate(Uri.parse(sanitizeUrl("https://www.chromium.org")));
+            getNavigationController(0).navigate(Uri.parse(sanitizeUrl("https://www.google.com")));
+            getNavigationController(1).navigate(Uri.parse(sanitizeUrl("https://en.wikipedia.org")));
+            getNavigationController(2).navigate(Uri.parse(sanitizeUrl("https://www.chromium.org")));
         }
     }
 
     @Override
-    protected void onDestroy() {
-        for (int i = 0; i < mBrowserFragmentControllers.length; ++i) {
-            BrowserFragmentController fragment = mBrowserFragmentControllers[i];
-            if (fragment != null) fragment.destroy();
-            mBrowserFragmentControllers[i] = null;
-        }
-        if (mProfile != null) mProfile.destroy();
-        super.onDestroy();
-    }
-
-    @Override
     protected void onStart() {
         super.onStart();
     }
@@ -252,16 +244,24 @@
             mContainerViews[1].setOnClickListener(new OnClickImpl());
             mContainerViews[2].setOnClickListener(new OnClickImpl());
             mContainerViews[0].post(() -> {
-                mBrowserFragmentControllers[0].setSupportsEmbedding(true).addCallback(
+                getFragmentController(0).setSupportsEmbedding(true).addCallback(
                         (Boolean result) -> animateDown(mContainerViews[0]));
-                mBrowserFragmentControllers[1].setSupportsEmbedding(true).addCallback(
+                getFragmentController(1).setSupportsEmbedding(true).addCallback(
                         (Boolean result) -> animateDown(mContainerViews[1]));
-                mBrowserFragmentControllers[2].setSupportsEmbedding(true).addCallback(
+                getFragmentController(2).setSupportsEmbedding(true).addCallback(
                         (Boolean result) -> animateDown(mContainerViews[2]));
             });
         }
     }
 
+    private BrowserFragmentController getFragmentController(int index) {
+        return mBrowserFragments[index].getController();
+    }
+
+    private NavigationController getNavigationController(int index) {
+        return getFragmentController(index).getBrowserController().getNavigationController();
+    }
+
     private ContainerFrameLayout mFullscreenContainer;
     private MyWebView mFullscreenWebView;
     public class OnClickImpl implements View.OnClickListener {
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/FragmentRestoreTest.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/FragmentRestoreTest.java
new file mode 100644
index 0000000..f8fd29a
--- /dev/null
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/FragmentRestoreTest.java
@@ -0,0 +1,35 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.weblayer.test;
+
+import android.support.test.filters.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.chromium.base.test.BaseJUnit4ClassRunner;
+
+@RunWith(BaseJUnit4ClassRunner.class)
+public class FragmentRestoreTest {
+    @Rule
+    public WebLayerShellActivityTestRule mActivityTestRule = new WebLayerShellActivityTestRule();
+
+    @Test
+    @SmallTest
+    public void successfullyLoadsUrlAfterRotation() {
+        mActivityTestRule.launchShellWithUrl("about:blank");
+
+        String url = "data:text,foo";
+        mActivityTestRule.loadUrl(url);
+        mActivityTestRule.waitForNavigation(url);
+
+        mActivityTestRule.rotateActivity();
+
+        url = "data:text,bar";
+        mActivityTestRule.loadUrl(url);
+        mActivityTestRule.waitForNavigation(url);
+    }
+}
diff --git a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java
index 919310bb..78df02da 100644
--- a/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java
+++ b/weblayer/shell/android/javatests/src/org/chromium/weblayer/test/WebLayerShellActivityTestRule.java
@@ -4,8 +4,12 @@
 
 package org.chromium.weblayer.test;
 
+import android.app.Activity;
+import android.app.Instrumentation.ActivityMonitor;
 import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
 import android.net.Uri;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.rule.ActivityTestRule;
@@ -16,6 +20,8 @@
 import org.chromium.weblayer.NavigationController;
 import org.chromium.weblayer.shell.WebLayerShellActivity;
 
+import java.lang.reflect.Field;
+
 /**
  * ActivityTestRule for WebLayerShellActivity.
  *
@@ -64,4 +70,36 @@
     public void loadUrl(String url) {
         TestThreadUtils.runOnUiThreadBlocking(() -> { getActivity().loadUrl(url); });
     }
+
+    /**
+     * Rotates the Activity, blocking until the Activity is re-created.
+     * After calling this, getActivity() returns the new Activity.
+     */
+    public void rotateActivity() {
+        Activity activity = getActivity();
+
+        ActivityMonitor monitor = new ActivityMonitor(WebLayerShellActivity.class.getName(),
+                null, false);
+        InstrumentationRegistry.getInstrumentation().addMonitor(monitor);
+
+        int current = activity.getResources().getConfiguration().orientation;
+        int requested = current == Configuration.ORIENTATION_LANDSCAPE ?
+                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT :
+                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        activity.setRequestedOrientation(requested);
+        CriteriaHelper.pollUiThread(() ->
+            monitor.getLastActivity() != null && monitor.getLastActivity() != activity
+        );
+        InstrumentationRegistry.getInstrumentation().removeMonitor(monitor);
+
+        // There is no way to rotate the activity using ActivityTestRule or even notify it.
+        // So we have to hack...
+        try {
+            Field field = ActivityTestRule.class.getDeclaredField("mActivity");
+            field.setAccessible(true);
+            field.set(this, monitor.getLastActivity());
+        } catch (ReflectiveOperationException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/weblayer/shell/android/shell_apk/AndroidManifest.xml b/weblayer/shell/android/shell_apk/AndroidManifest.xml
index c49404df..3d5adf5 100644
--- a/weblayer/shell/android/shell_apk/AndroidManifest.xml
+++ b/weblayer/shell/android/shell_apk/AndroidManifest.xml
@@ -13,7 +13,6 @@
         <activity android:name="WebLayerShellActivity"
                   android:launchMode="singleTask"
                   android:theme="@android:style/Theme.Holo.Light.NoActionBar"
-                  android:configChanges="orientation|keyboardHidden|keyboard|screenSize"
                   android:windowSoftInputMode="adjustPan|stateUnspecified">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
index df44cd51..4e46a2b 100644
--- a/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
+++ b/weblayer/shell/android/shell_apk/src/org/chromium/weblayer/shell/WebLayerShellActivity.java
@@ -7,12 +7,15 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
 import android.text.InputType;
 import android.text.TextUtils;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
 import android.view.inputmethod.EditorInfo;
 import android.widget.EditText;
@@ -23,16 +26,21 @@
 import android.widget.TextView.OnEditorActionListener;
 
 import org.chromium.weblayer.BrowserController;
+import org.chromium.weblayer.BrowserFragment;
 import org.chromium.weblayer.BrowserFragmentController;
 import org.chromium.weblayer.BrowserObserver;
 import org.chromium.weblayer.Profile;
+import org.chromium.weblayer.UnsupportedVersionException;
 import org.chromium.weblayer.WebLayer;
 
+import java.util.List;
+
 /**
  * Activity for managing the Demo Shell.
  */
 public class WebLayerShellActivity extends FragmentActivity {
     private static final String TAG = "WebLayerShell";
+    private static final String KEY_MAIN_VIEW_ID = "mainViewId";
 
     private Profile mProfile;
     private BrowserFragmentController mBrowserFragmentController;
@@ -40,6 +48,9 @@
     private EditText mUrlView;
     private ProgressBar mLoadProgressBar;
     private View mMainView;
+    private int mMainViewId;
+    private ViewGroup mTopContentsContainer;
+    private BrowserFragment mFragment;
 
     public BrowserController getBrowserController() {
         return mBrowserController;
@@ -52,17 +63,13 @@
     @Override
     protected void onCreate(final Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        WebLayer webLayer = null;
-        try {
-            webLayer = WebLayer.create(getApplication()).get();
-        } catch (Exception e) {
-            throw new RuntimeException("failed loading WebLayer", e);
-        }
-
         LinearLayout mainView = new LinearLayout(this);
-        int viewId = View.generateViewId();
-        mainView.setId(viewId);
+        if (savedInstanceState == null) {
+            mMainViewId = View.generateViewId();
+        } else {
+            mMainViewId = savedInstanceState.getInt(KEY_MAIN_VIEW_ID);
+        }
+        mainView.setId(mMainViewId);
         mMainView = mainView;
         setContentView(mainView);
 
@@ -93,8 +100,8 @@
         mLoadProgressBar.setVisibility(View.INVISIBLE);
 
         // The progress bar sits above the URL bar in Z order and at its bottom in Y.
-        RelativeLayout topContentsContainer = new RelativeLayout(this);
-        topContentsContainer.addView(mUrlView,
+        mTopContentsContainer = new RelativeLayout(this);
+        mTopContentsContainer.addView(mUrlView,
                 new RelativeLayout.LayoutParams(
                         LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
 
@@ -102,17 +109,28 @@
                 LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
         progressLayoutParams.addRule(RelativeLayout.ALIGN_BOTTOM, mUrlView.getId());
         progressLayoutParams.setMargins(0, 0, 0, -10);
-        topContentsContainer.addView(mLoadProgressBar, progressLayoutParams);
+        mTopContentsContainer.addView(mLoadProgressBar, progressLayoutParams);
 
-        mProfile = webLayer.createProfile(null);
+        try {
+            // This ensures asynchronous initialization of WebLayer on first start of activity.
+            // If activity is re-created during process restart, FragmentManager attaches
+            // BrowserFragment immediately, resulting in synchronous init. By the time this line
+            // executes, the synchronous init has already happened.
+            WebLayer.create(getApplication()).addCallback(webLayer -> onWebLayerReady(
+                    savedInstanceState));
+        } catch (UnsupportedVersionException e) {
+            throw new RuntimeException("Failed to initialize WebLayer", e);
+        }
+    }
 
-        mBrowserFragmentController = mProfile.createBrowserFragmentController(this);
+    private void onWebLayerReady(Bundle savedInstanceState) {
+        if (isFinishing() || isDestroyed()) return;
 
-        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
-        transaction.add(viewId, mBrowserFragmentController.getFragment());
-        transaction.commit();
+        mFragment = getOrCreateBrowserFragment(savedInstanceState);
+        mBrowserFragmentController = mFragment.getController();
+        mProfile = mBrowserFragmentController.getProfile();
 
-        mBrowserFragmentController.setTopView(topContentsContainer);
+        mBrowserFragmentController.setTopView(mTopContentsContainer);
 
         mBrowserController = mBrowserFragmentController.getBrowserController();
         String startupUrl = getUrlFromIntent(getIntent());
@@ -139,11 +157,28 @@
         });
     }
 
-    @Override
-    protected void onDestroy() {
-        if (mProfile != null) mProfile.destroy();
-        if (mBrowserFragmentController != null) mBrowserFragmentController.destroy();
-        super.onDestroy();
+    private BrowserFragment getOrCreateBrowserFragment(Bundle savedInstanceState) {
+        FragmentManager fragmentManager = getSupportFragmentManager();
+        if (savedInstanceState != null) {
+            // FragmentManager could have re-created the fragment.
+            List<Fragment> fragments = fragmentManager.getFragments();
+            if (fragments.size() > 1) {
+                throw new IllegalStateException("More than one fragment added, shouldn't happen");
+            }
+            if (fragments.size() == 1) {
+                return (BrowserFragment) fragments.get(0);
+            }
+        }
+
+        BrowserFragment fragment = WebLayer.createBrowserFragment(null);
+        FragmentTransaction transaction = fragmentManager.beginTransaction();
+        transaction.add(mMainViewId, fragment);
+
+        // Note the commitNow() instead of commit(). We want the fragment to get attached to
+        // activity synchronously, so we can use all the functionality immediately. Otherwise we'd
+        // have to wait until the commit is executed.
+        transaction.commitNow();
+        return fragment;
     }
 
     @Override
@@ -170,4 +205,12 @@
         if (url.startsWith("www.") || url.indexOf(":") == -1) url = "http://" + url;
         return url;
     }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        // When restoring Fragments, FragmentManager tries to put them in the containers with same
+        // ids as before.
+        outState.putInt(KEY_MAIN_VIEW_ID, mMainViewId);
+    }
 }