diff --git a/AUTHORS b/AUTHORS
index 7ffcaba..265d1aa 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -645,6 +645,7 @@
 Md Jobed Hossain <jrony15@gmail.com>
 Md Sami Uddin <md.sami@samsung.com>
 Michael Cirone <mikecirone@gmail.com>
+Michael Constant <mconst@gmail.com>
 Michael Forney <mforney@mforney.org>
 Michael Gilbert <floppymaster@gmail.com>
 Michael Lopez <lopes92290@gmail.com>
diff --git a/BUILD.gn b/BUILD.gn
index a221d1c..f4df720a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -910,6 +910,8 @@
 }
 
 if (is_chromeos) {
+  # This group is used to collect all targets used when verifying a new version
+  # of Chrome for Chrome OS.
   group("chromiumos_preflight") {
     testonly = true
     deps = [
@@ -917,15 +919,20 @@
       "//chrome/browser/metrics/perf:profile_provider_unittest",
       "//chrome/test/chromedriver",
       "//components/chromeos_camera:jpeg_decode_accelerator_unittest",
+      "//components/exo/wayland:wayland_client_perftests",
       "//media:media_unittests",
+      "//media/capture:capture_unittests",
       "//ppapi/examples/video_decode",
       "//sandbox/linux:chrome_sandbox",
       "//sandbox/linux:sandbox_linux_unittests",
       "//third_party/breakpad:minidump_stackwalk($host_toolchain)",
+      "//third_party/dawn:dawn_end2end_tests",
+      "//third_party/dawn:dawn_unittests",
 
       # Blocked on https://github.com/catapult-project/catapult/issues/2297
       #"//third_party/catapult/telemetry:bitmaptools",
       "//tools/perf/clear_system_cache",
+      "//ui/ozone/gl:ozone_gl_unittests",
     ]
 
     if (use_v4l2_codec || use_vaapi) {
@@ -935,6 +942,9 @@
         "//media/gpu:video_decode_accelerator_tests",
         "//media/gpu:video_encode_accelerator_unittest",
       ]
+      if (use_vaapi) {
+        deps += [ "//media/gpu/vaapi:vaapi_unittest" ]
+      }
     }
   }
 }
diff --git a/DEPS b/DEPS
index f851cd7..f64912bd 100644
--- a/DEPS
+++ b/DEPS
@@ -175,7 +175,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling Skia
   # and whatever else without interference from each other.
-  'skia_revision': 'e9bc857b39c22878fd2f46284d83f22fe45540d9',
+  'skia_revision': '46de36f99362b94a04abbea3b7391c2d82602c80',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling V8
   # and whatever else without interference from each other.
@@ -191,7 +191,7 @@
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling SwiftShader
   # and whatever else without interference from each other.
-  'swiftshader_revision': '1c29477aa06ea733994d1d11d6bf63ba27ac8359',
+  'swiftshader_revision': '81d8c2ada561340400ccda0c560dc8f61ffb9a30',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling PDFium
   # and whatever else without interference from each other.
@@ -298,7 +298,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.
-  'shaderc_revision': '9472e3eec08587a1c6616fd64d0d38553e9efd04',
+  'shaderc_revision': 'd52066592790a4cfd9d17e9154456ab2e1352b40',
   # Three lines of non-changing comments so that
   # the commit queue can handle CLs rolling feed
   # and whatever else without interference from each other.
@@ -852,7 +852,7 @@
 
   # Build tools for Chrome OS. Note: This depends on third_party/pyelftools.
   'src/third_party/chromite': {
-      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + 'f4f16eaf072eb549e0ae71a6a705517e2fda8bac',
+      'url': Var('chromium_git') + '/chromiumos/chromite.git' + '@' + '9a8735962f3eb9a2a980bf01dc86b68260b3924f',
       'condition': 'checkout_linux',
   },
 
@@ -877,7 +877,7 @@
   },
 
   'src/third_party/depot_tools':
-    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '6a7e234b584eff3fbbd5686f5ec75cba3d25667c',
+    Var('chromium_git') + '/chromium/tools/depot_tools.git' + '@' + '11f4a84bb1b3918230d025c91f3331d21411a22b',
 
   'src/third_party/devtools-frontend/src':
     Var('chromium_git') + '/devtools/devtools-frontend' + '@' + Var('devtools_frontend_revision'),
@@ -933,7 +933,7 @@
     Var('chromium_git') + '/codecs/libgav1.git' + '@' + 'fa1c3c4e673cf12ffa22b8fbe4a7c79314571f1b',
 
   'src/third_party/glslang/src':
-    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '344bd0889ac98c5369df2bf359d0369e3d235b62',
+    Var('chromium_git') + '/external/github.com/KhronosGroup/glslang.git' + '@' + '08c02ced798afe357349d0e422cd474aa1eb0c79',
 
   'src/third_party/google_toolbox_for_mac/src': {
       'url': Var('chromium_git') + '/external/github.com/google/google-toolbox-for-mac.git' + '@' + Var('google_toolbox_for_mac_revision'),
@@ -1448,7 +1448,7 @@
     Var('chromium_git') + '/external/github.com/gpuweb/cts.git' + '@' + '84ee597cdeae08bb26e578fc66a35bcf35f633f4',
 
   'src/third_party/webrtc':
-    Var('webrtc_git') + '/src.git' + '@' + '7bd282acce658a1a640964e90dc2c54e8f3b1530',
+    Var('webrtc_git') + '/src.git' + '@' + 'c24b6b7815ceada81afbd75e77339fefb27b7846',
 
   'src/third_party/libgifcodec':
      Var('skia_git') + '/libgifcodec' + '@'+  Var('libgifcodec_revision'),
@@ -1523,7 +1523,7 @@
     Var('chromium_git') + '/v8/v8.git' + '@' +  Var('v8_revision'),
 
   'src-internal': {
-    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@8df96b104065304c1bb7bb58a1394c59063be2c1',
+    'url': 'https://chrome-internal.googlesource.com/chrome/src-internal.git@012fbeb999c62cfb9e498fca8b7cb7e4488077c7',
     'condition': 'checkout_src_internal',
   },
 
diff --git a/WATCHLISTS b/WATCHLISTS
index da3ebb8..9ac2791d 100644
--- a/WATCHLISTS
+++ b/WATCHLISTS
@@ -1110,11 +1110,6 @@
                   'content/test/data/indexeddb|'\
                   'components/services/storage/indexed_db',
     },
-    'infra': {
-      'filepath': 'infra/config|'\
-                  'testing/buildbot|'\
-                  'tools/mb',
-    },
     'infra_docs': {
       'filepath': 'docs/infra',
     },
@@ -2052,8 +2047,7 @@
     'android_deps': ['wnwen+watch@chromium.org'],
     'android_flags': ['hnakashima+watch@chromium.org'],
     'android_infobars': ['dfalcantara+watch@chromium.org'],
-    'android_infra': ['agrieve+watch@chromium.org',
-                      'jbudorick+watch@chromium.org'],
+    'android_infra': ['agrieve+watch@chromium.org'],
     'android_item_chooser_dialogs': ['juncai+watch@chromium.org',
                                      'ortuno+watch@chromium.org'],
     'android_lint': ['wnwen+watch@chromium.org'],
@@ -2493,7 +2487,6 @@
     'incident_reporting': ['grt+watch@chromium.org'],
     'indexed_db': ['jsbell+idb@chromium.org',
                    'dmurph+watching-idb@chromium.org'],
-    'infra': ['jbudorick+watch@chromium.org'],
     'infra_docs': ['martiniss+watch@chromium.org'],
     'input': ['dtapuska+chromiumwatch@chromium.org',
               'nzolghadr+chromiumwatch@chromium.org'],
diff --git a/android_webview/nonembedded/BUILD.gn b/android_webview/nonembedded/BUILD.gn
index 4266ce4..647bb88 100644
--- a/android_webview/nonembedded/BUILD.gn
+++ b/android_webview/nonembedded/BUILD.gn
@@ -41,7 +41,6 @@
     "java/src/org/chromium/android_webview/devui/PersistentErrorView.java",
     "java/src/org/chromium/android_webview/devui/WebViewPackageError.java",
     "java/src/org/chromium/android_webview/devui/util/CrashInfoLoader.java",
-    "java/src/org/chromium/android_webview/devui/util/NavigationMenuHelper.java",
     "java/src/org/chromium/android_webview/devui/util/UnuploadedFilesStateLoader.java",
     "java/src/org/chromium/android_webview/devui/util/UploadedCrashesInfoLoader.java",
     "java/src/org/chromium/android_webview/devui/util/WebViewCrashInfoCollector.java",
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/FlagsFragment.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/FlagsFragment.java
index 843a43a..5ba4c50 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/FlagsFragment.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/FlagsFragment.java
@@ -215,7 +215,6 @@
      * @param state the state of the flag.
      */
     private void formatListEntry(View toggleableFlag, int state) {
-        // View stateIndicator = toggleableFlag.findViewById(R.id.flag_state_indicator);
         TextView flagName = toggleableFlag.findViewById(R.id.flag_name);
         if (state == /* STATE_DEFAULT */ 0) {
             // Unset the compound drawable.
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java
index 8f5394b..a5cb5563 100644
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java
+++ b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/MainActivity.java
@@ -7,7 +7,9 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -19,7 +21,6 @@
 import androidx.fragment.app.FragmentManager;
 import androidx.fragment.app.FragmentTransaction;
 
-import org.chromium.android_webview.devui.util.NavigationMenuHelper;
 import org.chromium.base.ApiCompatibilityUtils;
 
 import java.util.HashMap;
@@ -156,13 +157,17 @@
 
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
-        NavigationMenuHelper.inflate(this, menu);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            getMenuInflater().inflate(R.menu.navigation_menu, menu);
+        }
         return true;
     }
 
     @Override
     public boolean onOptionsItemSelected(MenuItem item) {
-        if (NavigationMenuHelper.onOptionsItemSelected(this, item)) {
+        if (item.getItemId() == R.id.nav_menu_switch_provider
+                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            startActivity(new Intent(Settings.ACTION_WEBVIEW_SETTINGS));
             return true;
         }
         return super.onOptionsItemSelected(item);
diff --git a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/util/NavigationMenuHelper.java b/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/util/NavigationMenuHelper.java
deleted file mode 100644
index 296adf9..0000000
--- a/android_webview/nonembedded/java/src/org/chromium/android_webview/devui/util/NavigationMenuHelper.java
+++ /dev/null
@@ -1,52 +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.
-package org.chromium.android_webview.devui.util;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Build;
-import android.provider.Settings;
-import android.view.Menu;
-import android.view.MenuItem;
-
-import androidx.fragment.app.FragmentActivity;
-
-import org.chromium.android_webview.devui.R;
-
-/**
- * Helper class for menu to access external tools. Built-in tools should be handled by Fragments in
- * the MainActivity.
- */
-public final class NavigationMenuHelper {
-    /**
-     * Inflate the navigation menu in the given {@code activity} options menu.
-     *
-     * This should be called inside {@link Activity#onCreateOptionsMenu} or
-     * {@link android.support.v4.app.Fragment#onCreateOptionsMenu}.
-     */
-    public static void inflate(Activity activity, Menu menu) {
-        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
-            return;
-        }
-        activity.getMenuInflater().inflate(R.menu.navigation_menu, menu);
-    }
-
-    /**
-     * Handle {@code onOptionsItemSelected} event for navigation menu.
-     *
-     * This should be called inside {@code Activity#onOptionsItemSelected} method.
-     * @return {@code true} if the item selection event is consumed.
-     */
-    public static boolean onOptionsItemSelected(FragmentActivity activity, MenuItem item) {
-        if (item.getItemId() == R.id.nav_menu_switch_provider
-                && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
-            activity.startActivity(new Intent(Settings.ACTION_WEBVIEW_SETTINGS));
-            return true;
-        }
-        return false;
-    }
-
-    // Do not instantiate this class.
-    private NavigationMenuHelper() {}
-}
diff --git a/ash/assistant/test/assistant_ash_test_base.cc b/ash/assistant/test/assistant_ash_test_base.cc
index f56006af..993cda1 100644
--- a/ash/assistant/test/assistant_ash_test_base.cc
+++ b/ash/assistant/test/assistant_ash_test_base.cc
@@ -17,6 +17,7 @@
 #include "ash/public/cpp/assistant/assistant_state.h"
 #include "ash/public/cpp/test/assistant_test_api.h"
 #include "ash/shell.h"
+#include "ash/test/ash_test_helper.h"
 #include "base/run_loop.h"
 #include "base/test/task_environment.h"
 
diff --git a/ash/resources/vector_icons/BUILD.gn b/ash/resources/vector_icons/BUILD.gn
index 28592d7..04df7f8 100644
--- a/ash/resources/vector_icons/BUILD.gn
+++ b/ash/resources/vector_icons/BUILD.gn
@@ -37,6 +37,7 @@
     "dictation_off_newui.icon",
     "dictation_on.icon",
     "dictation_on_newui.icon",
+    "hollow_check_circle.icon",
     "ime_menu_emoticon.icon",
     "ime_menu_microphone.icon",
     "ime_menu_on_screen_keyboard.icon",
diff --git a/ash/resources/vector_icons/hollow_check_circle.icon b/ash/resources/vector_icons/hollow_check_circle.icon
new file mode 100644
index 0000000..f77e517
--- /dev/null
+++ b/ash/resources/vector_icons/hollow_check_circle.icon
@@ -0,0 +1,24 @@
+CANVAS_DIMENSIONS, 20,
+MOVE_TO, 13.71f, 7.29f,
+CUBIC_TO, 13.32f, 6.9f, 12.68f, 6.9f, 12.29f, 7.29f,
+LINE_TO, 9, 10.59f,
+LINE_TO, 7.71f, 9.29f,
+CUBIC_TO, 7.32f, 8.9f, 6.68f, 8.9f, 6.29f, 9.29f,
+CUBIC_TO, 5.9f, 9.68f, 5.9f, 10.32f, 6.29f, 10.71f,
+LINE_TO, 8.29f, 12.71f,
+CUBIC_TO, 8.68f, 13.1f, 9.32f, 13.1f, 9.71f, 12.71f,
+LINE_TO, 13.71f, 8.71f,
+CUBIC_TO, 14.1f, 8.32f, 14.1f, 7.68f, 13.71f, 7.29f,
+CLOSE,
+MOVE_TO, 10, 18,
+CUBIC_TO, 14.42f, 18, 18, 14.42f, 18, 10,
+CUBIC_TO, 18, 5.58f, 14.42f, 2, 10, 2,
+CUBIC_TO, 5.58f, 2, 2, 5.58f, 2, 10,
+CUBIC_TO, 2, 14.42f, 5.58f, 18, 10, 18,
+CLOSE,
+MOVE_TO, 10, 16,
+CUBIC_TO, 13.31f, 16, 16, 13.31f, 16, 10,
+CUBIC_TO, 16, 6.69f, 13.31f, 4, 10, 4,
+CUBIC_TO, 6.69f, 4, 4, 6.69f, 4, 10,
+CUBIC_TO, 4, 13.31f, 6.69f, 16, 10, 16,
+CLOSE
\ No newline at end of file
diff --git a/ash/system/audio/unified_volume_view.cc b/ash/system/audio/unified_volume_view.cc
index 5b5c27d..e143b58b 100644
--- a/ash/system/audio/unified_volume_view.cc
+++ b/ash/system/audio/unified_volume_view.cc
@@ -4,6 +4,7 @@
 
 #include "ash/system/audio/unified_volume_view.h"
 
+#include "ash/public/cpp/ash_features.h"
 #include "ash/resources/vector_icons/vector_icons.h"
 #include "ash/strings/grit/ash_strings.h"
 #include "ash/style/ash_color_provider.h"
@@ -71,11 +72,14 @@
     const SkColor icon_color = AshColorProvider::Get()->GetContentLayerColor(
         AshColorProvider::ContentLayerType::kIconPrimary,
         AshColorProvider::AshColorMode::kDark);
-    auto* headset = new views::ImageView();
-    headset->set_can_process_events_within_subtree(false);
-    headset->SetImage(CreateVectorIcon(vector_icons::kHeadsetIcon, icon_color));
-    AddChildView(headset);
 
+    if (!features::IsSystemTrayMicGainSettingEnabled()) {
+      auto* headset = new views::ImageView();
+      headset->set_can_process_events_within_subtree(false);
+      headset->SetImage(
+          CreateVectorIcon(vector_icons::kHeadsetIcon, icon_color));
+      AddChildView(headset);
+    }
     auto* more = new views::ImageView();
     more->set_can_process_events_within_subtree(false);
     auto icon_rotation = base::i18n::IsRTL()
@@ -172,7 +176,8 @@
       IDS_ASH_STATUS_TRAY_VOLUME, state_tooltip_text));
 
   more_button_->SetVisible(CrasAudioHandler::Get()->has_alternative_input() ||
-                           CrasAudioHandler::Get()->has_alternative_output());
+                           CrasAudioHandler::Get()->has_alternative_output() ||
+                           features::IsSystemTrayMicGainSettingEnabled());
 
   // Slider's value is in finer granularity than audio volume level(0.01),
   // there will be a small discrepancy between slider's value and volume level
diff --git a/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc b/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
index 51f65d9d..f0ac7de3 100644
--- a/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
+++ b/ash/system/bluetooth/tray_bluetooth_helper_legacy_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/system/bluetooth/tray_bluetooth_helper.h"
 #include "ash/test/ash_test_base.h"
 #include "base/bind_helpers.h"
+#include "base/command_line.h"
 #include "base/run_loop.h"
 #include "base/strings/utf_string_conversions.h"
 #include "chromeos/constants/chromeos_switches.h"
diff --git a/ash/system/power/peripheral_battery_notifier_unittest.cc b/ash/system/power/peripheral_battery_notifier_unittest.cc
index 74b3833..a5812fa 100644
--- a/ash/system/power/peripheral_battery_notifier_unittest.cc
+++ b/ash/system/power/peripheral_battery_notifier_unittest.cc
@@ -10,6 +10,7 @@
 #include "ash/test/ash_test_base.h"
 #include "base/macros.h"
 #include "base/strings/string16.h"
+#include "base/strings/string_number_conversions.h"
 #include "base/strings/string_piece.h"
 #include "base/strings/string_util.h"
 #include "base/strings/utf_string_conversions.h"
diff --git a/ash/system/tray/tray_detailed_view.cc b/ash/system/tray/tray_detailed_view.cc
index f7f43255..1ed4ee4 100644
--- a/ash/system/tray/tray_detailed_view.cc
+++ b/ash/system/tray/tray_detailed_view.cc
@@ -393,8 +393,9 @@
 
   views::ImageView* image_view = TrayPopupUtils::CreateMainImageView();
   image_view->SetImage(gfx::CreateVectorIcon(
-      icon, GetNativeTheme()->GetSystemColor(
-                ui::NativeTheme::kColorId_ProminentButtonColor)));
+      icon, AshColorProvider::Get()->GetContentLayerColor(
+                AshColorProvider::ContentLayerType::kIconPrimary,
+                AshColorProvider::AshColorMode::kDark)));
   header->AddView(TriView::Container::START, image_view);
 
   scroll_content_->AddChildView(header);
diff --git a/ash/system/tray/tray_popup_utils.cc b/ash/system/tray/tray_popup_utils.cc
index 737828af..463460e 100644
--- a/ash/system/tray/tray_popup_utils.cc
+++ b/ash/system/tray/tray_popup_utils.cc
@@ -363,7 +363,7 @@
                                               bool enterprise_managed) {
   const int dip_size = GetDefaultSizeOfVectorIcon(kCheckCircleIcon);
   gfx::ImageSkia check_mark =
-      CreateVectorIcon(kCheckCircleIcon, dip_size, gfx::kGoogleGreenDark600);
+      CreateVectorIcon(kHollowCheckCircleIcon, dip_size, gfx::kGoogleGreen300);
   if (enterprise_managed) {
     gfx::ImageSkia enterprise_managed_icon = CreateVectorIcon(
         kLoginScreenEnterpriseIcon, dip_size, gfx::kGoogleGrey100);
diff --git a/ash/system/unified/feature_pods_container_view_unittest.cc b/ash/system/unified/feature_pods_container_view_unittest.cc
index a535cf8..710b7e62 100644
--- a/ash/system/unified/feature_pods_container_view_unittest.cc
+++ b/ash/system/unified/feature_pods_container_view_unittest.cc
@@ -14,6 +14,8 @@
 #include "ash/system/unified/unified_system_tray_model.h"
 #include "ash/test/ash_test_base.h"
 #include "base/test/scoped_feature_list.h"
+#include "ui/accessibility/ax_enums.mojom.h"
+#include "ui/accessibility/ax_node_data.h"
 #include "ui/views/view_observer.h"
 
 namespace ash {
diff --git a/ash/test/ash_test_base.cc b/ash/test/ash_test_base.cc
index 39631cb4b..65767f5e 100644
--- a/ash/test/ash_test_base.cc
+++ b/ash/test/ash_test_base.cc
@@ -151,7 +151,8 @@
   params.start_session = start_session_;
   params.delegate = std::move(delegate);
   params.local_state = local_state();
-  ash_test_helper_.SetUp(std::move(params));
+  ash_test_helper_ = std::make_unique<AshTestHelper>();
+  ash_test_helper_->SetUp(std::move(params));
 
   Shell::GetPrimaryRootWindow()->Show();
   Shell::GetPrimaryRootWindow()->GetHost()->Show();
@@ -179,7 +180,7 @@
   // Flush the message loop to finish pending release tasks.
   base::RunLoop().RunUntilIdle();
 
-  ash_test_helper_.TearDown();
+  ash_test_helper_->TearDown();
 
   event_generator_.reset();
   // Some tests set an internal display id,
@@ -236,7 +237,7 @@
 }
 
 aura::Window* AshTestBase::GetContext() {
-  return ash_test_helper_.GetContext();
+  return ash_test_helper_->GetContext();
 }
 
 // static
@@ -382,15 +383,15 @@
 }
 
 TestSessionControllerClient* AshTestBase::GetSessionControllerClient() {
-  return ash_test_helper_.test_session_controller_client();
+  return ash_test_helper_->test_session_controller_client();
 }
 
 TestSystemTrayClient* AshTestBase::GetSystemTrayClient() {
-  return ash_test_helper_.system_tray_client();
+  return ash_test_helper_->system_tray_client();
 }
 
 AppListTestHelper* AshTestBase::GetAppListTestHelper() {
-  return ash_test_helper_.app_list_test_helper();
+  return ash_test_helper_->app_list_test_helper();
 }
 
 void AshTestBase::CreateUserSessions(int n) {
@@ -558,7 +559,7 @@
 }
 
 display::Display AshTestBase::GetSecondaryDisplay() const {
-  return ash_test_helper_.GetSecondaryDisplay();
+  return ash_test_helper_->GetSecondaryDisplay();
 }
 
 }  // namespace ash
diff --git a/ash/test/ash_test_base.h b/ash/test/ash_test_base.h
index 23d71fb..0c06e00 100644
--- a/ash/test/ash_test_base.h
+++ b/ash/test/ash_test_base.h
@@ -14,7 +14,6 @@
 #include "ash/public/cpp/app_types.h"
 #include "ash/public/cpp/shell_window_ids.h"
 #include "ash/session/test_session_controller_client.h"
-#include "ash/test/ash_test_helper.h"
 #include "ash/wm/desks/desks_util.h"
 #include "base/compiler_specific.h"
 #include "base/macros.h"
@@ -67,6 +66,7 @@
 namespace ash {
 
 class AppListTestHelper;
+class AshTestHelper;
 class Shelf;
 class TestScreenshotDelegate;
 class TestShellDelegate;
@@ -221,7 +221,7 @@
     return task_environment_.get();
   }
   TestingPrefServiceSimple* local_state() { return &local_state_; }
-  AshTestHelper* ash_test_helper() { return &ash_test_helper_; }
+  AshTestHelper* ash_test_helper() { return ash_test_helper_.get(); }
 
   TestScreenshotDelegate* GetScreenshotDelegate();
 
@@ -299,8 +299,8 @@
   // A pref service used for local state.
   TestingPrefServiceSimple local_state_;
 
-  // Must be constructed after |task_environment_| but before SetUp().
-  AshTestHelper ash_test_helper_;
+  // Must be constructed after |task_environment_|.
+  std::unique_ptr<AshTestHelper> ash_test_helper_;
 
   std::unique_ptr<ui::test::EventGenerator> event_generator_;
 
diff --git a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
index 5b8198d..43af7d38 100644
--- a/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
+++ b/ash/wm/gestures/back_gesture/back_gesture_event_handler.cc
@@ -162,6 +162,15 @@
 void BackGestureEventHandler::OnGestureEvent(ui::GestureEvent* event) {}
 
 void BackGestureEventHandler::OnTouchEvent(ui::TouchEvent* event) {
+  // Do not handle PEN and ERASER events for back gesture. PEN events can come
+  // from stylus device.
+  if (event->pointer_details().pointer_type ==
+          ui::EventPointerType::POINTER_TYPE_PEN ||
+      event->pointer_details().pointer_type ==
+          ui::EventPointerType::POINTER_TYPE_ERASER) {
+    return;
+  }
+
   if (first_touch_id_ == ui::kPointerIdUnknown)
     first_touch_id_ = event->pointer_details().id;
 
diff --git a/ash/wm/screen_pinning_controller.cc b/ash/wm/screen_pinning_controller.cc
index 2bf58dd..0ebdfe9 100644
--- a/ash/wm/screen_pinning_controller.cc
+++ b/ash/wm/screen_pinning_controller.cc
@@ -157,6 +157,9 @@
 
 ScreenPinningController::~ScreenPinningController() {
   Shell::Get()->window_tree_host_manager()->RemoveObserver(this);
+  if (pinned_window_)
+    pinned_window_->RemoveObserver(this);
+  pinned_window_ = nullptr;
 }
 
 bool ScreenPinningController::IsPinned() const {
@@ -177,6 +180,8 @@
 
     // Set up the container which has the pinned window.
     pinned_window_ = pinned_window;
+    // To monitor destruction.
+    pinned_window_->AddObserver(this);
     AlwaysOnTopController::SetDisallowReparent(pinned_window);
     container->StackChildAtTop(pinned_window);
     container->StackChildBelow(CreateWindowDimmer(container), pinned_window);
@@ -219,6 +224,7 @@
     container->RemoveObserver(pinned_container_window_observer_.get());
 
     window_dimmers_->clear();
+    pinned_window_->RemoveObserver(this);
     pinned_window_ = nullptr;
   }
 
@@ -234,8 +240,6 @@
 void ScreenPinningController::OnWillRemoveWindowFromPinnedContainer(
     aura::Window* window) {
   window->RemoveObserver(pinned_container_child_window_observer_.get());
-  if (window == pinned_window_)
-    WindowState::Get(pinned_window_)->Restore();
 }
 
 void ScreenPinningController::OnPinnedContainerWindowStackingChanged(
@@ -311,6 +315,13 @@
   }
 }
 
+void ScreenPinningController::OnWindowDestroying(aura::Window* window) {
+  DCHECK_EQ(pinned_window_, window);
+  WindowState::Get(window)->Restore();
+  window->RemoveObserver(this);
+  pinned_window_ = nullptr;
+}
+
 void ScreenPinningController::KeepPinnedWindowOnTop() {
   if (in_restacking_)
     return;
diff --git a/ash/wm/screen_pinning_controller.h b/ash/wm/screen_pinning_controller.h
index ce07f905..2de6fba3 100644
--- a/ash/wm/screen_pinning_controller.h
+++ b/ash/wm/screen_pinning_controller.h
@@ -31,7 +31,8 @@
 // https://developer.android.com/about/versions/android-5.0.html#ScreenPinning
 // See also ArcKioskAppLauncher::CheckAndPinWindow().
 class ASH_EXPORT ScreenPinningController
-    : public WindowTreeHostManager::Observer {
+    : public WindowTreeHostManager::Observer,
+      aura::WindowObserver {
  public:
   ScreenPinningController();
   ~ScreenPinningController() override;
@@ -86,6 +87,9 @@
   // WindowTreeHostManager::Observer:
   void OnDisplayConfigurationChanged() override;
 
+  // aura::WindowObserver:
+  void OnWindowDestroying(aura::Window* window) override;
+
   // Pinned window should be on top in the parent window.
   aura::Window* pinned_window_ = nullptr;
 
diff --git a/ash/wm/screen_pinning_controller_unittest.cc b/ash/wm/screen_pinning_controller_unittest.cc
index 0dbe82e..f52fe24 100644
--- a/ash/wm/screen_pinning_controller_unittest.cc
+++ b/ash/wm/screen_pinning_controller_unittest.cc
@@ -165,4 +165,24 @@
   EXPECT_TRUE(Shell::Get()->screen_pinning_controller()->IsPinned());
 }
 
+TEST_F(ScreenPinningControllerTest, ExitUnifiedDisplay) {
+  display_manager()->SetUnifiedDesktopEnabled(true);
+
+  UpdateDisplay("400x300, 400x400");
+
+  aura::Window* w1 = CreateTestWindowInShellWithId(0);
+  wm::ActivateWindow(w1);
+  auto* window_state = WindowState::Get(w1);
+
+  window_util::PinWindow(w1, /*trusted=*/true);
+
+  EXPECT_TRUE(window_state->IsPinned());
+  EXPECT_TRUE(Shell::Get()->screen_pinning_controller()->IsPinned());
+
+  UpdateDisplay("200x200");
+
+  EXPECT_TRUE(window_state->IsPinned());
+  EXPECT_TRUE(Shell::Get()->screen_pinning_controller()->IsPinned());
+}
+
 }  // namespace ash
diff --git a/base/task/thread_pool/job_task_source_unittest.cc b/base/task/thread_pool/job_task_source_unittest.cc
index 9ead3cc9..28847ee 100644
--- a/base/task/thread_pool/job_task_source_unittest.cc
+++ b/base/task/thread_pool/job_task_source_unittest.cc
@@ -386,13 +386,7 @@
 
 // Verifies that a missing call to NotifyConcurrencyIncrease() causes a DCHECK
 // death after a timeout.
-// TODO(crbug.com/1064953): Flaky on Windows.
-#if defined(OS_WIN)
-#define MAYBE_InvalidConcurrency DISABLED_InvalidConcurrency
-#else
-#define MAYBE_InvalidConcurrency InvalidConcurrency
-#endif
-TEST_F(ThreadPoolJobTaskSourceTest, MAYBE_InvalidConcurrency) {
+TEST_F(ThreadPoolJobTaskSourceTest, InvalidConcurrency) {
   testing::FLAGS_gtest_death_test_style = "threadsafe";
 
   scoped_refptr<test::MockJobTask> job_task;
@@ -422,14 +416,7 @@
 
 // Verifies that a stale concurrency with no call to NotifyConcurrencyIncrease()
 // causes a DCHECK death after a timeout.
-  
-// TODO(crbug.com/1064953): Flaky on Windows.
-#if defined(OS_WIN)
-#define MAYBE_StaleConcurrency DISABLED_StaleConcurrency
-#else
-#define MAYBE_StaleConcurrency StaleConcurrency
-#endif
-TEST_F(ThreadPoolJobTaskSourceTest, MAYBE_StaleConcurrency) {
+TEST_F(ThreadPoolJobTaskSourceTest, StaleConcurrency) {
   testing::FLAGS_gtest_death_test_style = "threadsafe";
 
   auto task_source = MakeRefCounted<JobTaskSource>(
diff --git a/base/task/thread_pool/thread_group_unittest.cc b/base/task/thread_pool/thread_group_unittest.cc
index be386a77..d6099cc 100644
--- a/base/task/thread_pool/thread_group_unittest.cc
+++ b/base/task/thread_pool/thread_group_unittest.cc
@@ -806,13 +806,7 @@
 
 // Verify that finishing work outside of a job unblocks workers with a stale
 // max concurrency.
-// TODO(crbug.com/1064953): Flaky on Windows.
-#if defined(OS_WIN)
-#define MAYBE_JoinJobTaskSourceStaleConcurrency DISABLED_JoinJobTaskSourceStaleConcurrency
-#else
-#define MAYBE_JoinJobTaskSourceStaleConcurrency JoinJobTaskSourceStaleConcurrency
-#endif
-TEST_P(ThreadGroupTest, MAYBE_JoinJobTaskSourceStaleConcurrency) {
+TEST_P(ThreadGroupTest, JoinJobTaskSourceStaleConcurrency) {
   StartThreadGroup();
 
   WaitableEvent thread_running;
diff --git a/base/test/test_support_ios.mm b/base/test/test_support_ios.mm
index 14050a85..5fd78c88 100644
--- a/base/test/test_support_ios.mm
+++ b/base/test/test_support_ios.mm
@@ -110,8 +110,9 @@
   // test result parser analyzes console output.
   return !base::ShouldRunIOSUnittestsWithXCTest() &&
          !base::debug::BeingDebugged();
-#endif  // TARGET_IPHONE_SIMULATOR
+#else
   return NO;
+#endif  // TARGET_IPHONE_SIMULATOR
 }
 
 // Returns the path to the directory to store gtest output files.
diff --git a/base/util/memory_pressure/memory_pressure_voter.cc b/base/util/memory_pressure/memory_pressure_voter.cc
index 20f668f..56c2d70 100644
--- a/base/util/memory_pressure/memory_pressure_voter.cc
+++ b/base/util/memory_pressure/memory_pressure_voter.cc
@@ -7,6 +7,7 @@
 #include <numeric>
 
 #include "base/stl_util.h"
+#include "base/trace_event/trace_event.h"
 
 namespace util {
 
@@ -83,7 +84,34 @@
   if (new_vote)
     votes_[new_vote.value()]++;
   auto old_pressure_level = current_pressure_level_;
+
+  // If the pressure level is not None then an asynchronous event will have been
+  // started below, it needs to be ended.
+  // Note that we record this event every time we receive a new vote to ensure
+  // that the begin event doesn't get dropped during long pressure sessions.
+  if (old_pressure_level ==
+      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL) {
+    TRACE_EVENT_NESTABLE_ASYNC_END0("base", "MemoryPressure::CriticalPressure",
+                                    this);
+  } else if (old_pressure_level ==
+             MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE) {
+    TRACE_EVENT_NESTABLE_ASYNC_END0("base", "MemoryPressure::ModeratePressure",
+                                    this);
+  }
+
   current_pressure_level_ = EvaluateVotes();
+
+  // Start an asynchronous tracing event to record this pressure session.
+  if (current_pressure_level_ ==
+      MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_CRITICAL) {
+    TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("base",
+                                      "MemoryPressure::CriticalPressure", this);
+  } else if (current_pressure_level_ ==
+             MemoryPressureLevel::MEMORY_PRESSURE_LEVEL_MODERATE) {
+    TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("base",
+                                      "MemoryPressure::ModeratePressure", this);
+  }
+
   if (old_pressure_level != current_pressure_level_)
     delegate_->OnMemoryPressureLevelChanged(current_pressure_level_);
 }
diff --git a/build/android/gyp/compile_resources.py b/build/android/gyp/compile_resources.py
index c18180d..36cb6ee 100755
--- a/build/android/gyp/compile_resources.py
+++ b/build/android/gyp/compile_resources.py
@@ -32,6 +32,7 @@
 from util import build_utils
 from util import diff_utils
 from util import manifest_utils
+from util import md5_check
 from util import resource_utils
 
 # `Resources_pb2` module imports `descriptor`, which imports `six`.
@@ -1099,11 +1100,7 @@
       shutil.move(temp, final)
 
 
-def main(args):
-  build_utils.InitLogging('RESOURCE_DEBUG')
-  args = build_utils.ExpandFileArgs(args)
-  options = _ParseArgs(args)
-
+def _OnStaleMd5(options):
   path = options.arsc_path or options.proto_path
   debug_temp_resources_dir = os.environ.get('TEMP_RESOURCES_DIR')
   if debug_temp_resources_dir:
@@ -1180,12 +1177,69 @@
     logging.debug('Copying outputs')
     _WriteOutputs(options, build)
 
-  if options.depfile:
-    build_utils.WriteDepfile(
-        options.depfile,
-        options.srcjar_out,
-        inputs=options.dependencies_res_zips + options.extra_r_text_files,
-        add_pydeps=False)
+
+def main(args):
+  build_utils.InitLogging('RESOURCE_DEBUG')
+  args = build_utils.ExpandFileArgs(args)
+  options = _ParseArgs(args)
+
+  depfile_deps = options.dependencies_res_zips + options.extra_r_text_files
+
+  input_paths = depfile_deps
+  input_strings = [
+      options.include_resources,
+      options.extra_res_packages,
+      options.extra_main_r_text_files,
+      options.min_sdk_version,
+      options.target_sdk_version,
+      options.webp_cache_dir,
+      options.no_xml_namespaces,
+      options.version_code,
+      options.version_name,
+      options.r_java_root_package_name,
+      options.strip_resource_names,
+      options.debuggable,
+      options.rename_manifest_package,
+      options.shared_resources,
+      options.app_as_shared_lib,
+      options.package_id,
+      options.package_name,
+      options.arsc_package_name,
+      options.shared_resources_allowlist_locales,
+      options.locale_allowlist,
+      options.png_to_webp,
+      options.resource_exclusion_regex,
+      options.resource_exclusion_exceptions,
+      options.support_zh_hk,
+      options.include_resources,
+      options.max_sdk_version,
+      options.manifest_package,
+  ]
+  possible_output_paths = [
+      options.srcjar_out,
+      options.r_text_out,
+      options.arsc_path,
+      options.proto_path,
+      options.optimized_arsc_path,
+      options.optimized_proto_path,
+      options.proguard_file,
+      options.proguard_file_main_dex,
+      options.emit_ids_out,
+      options.info_path,
+  ]
+  output_paths = [p for p in possible_output_paths if p]
+
+  # Since we overspecify deps, this target depends on java deps that are not
+  # going to change its output. This target is also slow (6-12 seconds) and
+  # blocking the critical path. We want changes to java_library targets to not
+  # trigger re-compilation of resources, thus we need to use md5_check.
+  md5_check.CallAndWriteDepfileIfStale(
+      lambda: _OnStaleMd5(options),
+      options,
+      input_paths=input_paths,
+      input_strings=input_strings,
+      output_paths=output_paths,
+      depfile_deps=depfile_deps)
 
 
 if __name__ == '__main__':
diff --git a/build/android/gyp/compile_resources.pydeps b/build/android/gyp/compile_resources.pydeps
index 60f6edb..53282f7 100644
--- a/build/android/gyp/compile_resources.pydeps
+++ b/build/android/gyp/compile_resources.pydeps
@@ -54,4 +54,5 @@
 util/build_utils.py
 util/diff_utils.py
 util/manifest_utils.py
+util/md5_check.py
 util/resource_utils.py
diff --git a/build/config/android/config.gni b/build/config/android/config.gni
index ccec0f16..afa18677 100644
--- a/build/config/android/config.gni
+++ b/build/config/android/config.gni
@@ -50,6 +50,12 @@
 
     # Android API level for 64 bits platforms
     android64_ndk_api_level = 21
+
+    # [WIP] Allows devs to achieve much faster edit-build-install cycles.
+    # Currently only works for ChromeModern apks due to incremental install.
+    # This needs to be in a separate declare_args as it determines some of the
+    # args in the main declare_args block below.
+    android_fast_local_dev = false
   }
 
   if (enable_chrome_android_internal) {
@@ -172,11 +178,12 @@
     # Set to false to disable the Errorprone compiler.
     # Defaults to false for official builds to reduce build times.
     # Static analysis failures should have been already caught by normal bots.
-    use_errorprone_java_compiler = !is_official_build
+    # Disabled when fast_local_dev is turned on.
+    use_errorprone_java_compiler = !is_official_build && !android_fast_local_dev
 
     # Build incremental targets whenever possible.
     # See //build/android/incremental_install/README.md for more details.
-    incremental_install = false
+    incremental_install = android_fast_local_dev
 
     # When true, updates all android_aar_prebuilt() .info files during gn gen.
     # Refer to android_aar_prebuilt() for more details.
@@ -185,7 +192,8 @@
     # Turns off android lint. Useful for prototyping or for faster local builds.
     # Defaults to true for official builds to reduce build times.
     # Static analysis failures should have been already caught by normal bots.
-    disable_android_lint = is_official_build
+    # Disabled when fast_local_dev is turned on.
+    disable_android_lint = is_official_build || android_fast_local_dev
 
     # Location of aapt2 used for app bundles. For now, a more recent version
     # than the one distributed with the Android SDK is required.
diff --git a/build/config/android/internal_rules.gni b/build/config/android/internal_rules.gni
index b275df4..92e8b99 100644
--- a/build/config/android/internal_rules.gni
+++ b/build/config/android/internal_rules.gni
@@ -3168,7 +3168,7 @@
       }
     }
 
-    _accumulated_public_deps = []
+    _public_deps = []
     _accumulated_deps = []
     _java_header_deps = []
     _java_full_deps = []
@@ -3362,7 +3362,6 @@
         }
       }
     }
-    _accumulated_public_deps += [ ":$_build_config_target_name" ]
 
     # Don't need to depend on the apk-under-test to be packaged.
     if (defined(invoker.apk_under_test)) {
@@ -3416,8 +3415,8 @@
           chromium_code = _chromium_code
           supports_android = _supports_android
           requires_android = _requires_android
-          deps =
-              _java_header_deps + _accumulated_deps + _accumulated_public_deps
+          deps = _java_header_deps + _accumulated_deps +
+                 [ ":$_build_config_target_name" ]
 
           # android_apk and junit_binary pass R.java srcjars via srcjar_deps.
           if (_type == "java_library" && _requires_android) {
@@ -3477,7 +3476,7 @@
           srcjar_deps = _srcjar_deps
           build_config = _build_config
           requires_android = _requires_android
-          deps = _accumulated_deps + _accumulated_public_deps
+          deps = _accumulated_deps + [ ":$_build_config_target_name" ]
           java_files = _java_files
           if (_java_files != []) {
             java_sources_file = _java_sources_file
@@ -3488,9 +3487,6 @@
         }
         _analysis_public_deps += [ ":$_android_lint_target" ]
       }
-
-      # Update this after lint so that lint does not depend on javac.
-      _accumulated_public_deps += [ ":$_compile_java_target" ]
     }  # _has_sources
 
     if (_is_prebuilt || _has_sources) {
@@ -3514,13 +3510,13 @@
           output_jar = _final_ijar_path
 
           # Some prebuilts have java deps (e.g. //third_party/proguard:retrace_java).
-          deps = _accumulated_deps + _java_header_deps
+          deps = _java_header_deps + _accumulated_deps
           if (_has_sources) {
             deps += [ ":$_compile_java_target" ]
           }
         }
       }
-      _accumulated_public_deps += [ ":$_header_target_name" ]
+      _public_deps += [ ":$_header_target_name" ]
     }  # _is_prebuilt || _has_sources
 
     if (defined(_final_jar_path)) {
@@ -3534,7 +3530,7 @@
           dest = _final_jar_path
           outputs = [ _final_jar_path ]
         }
-        _accumulated_public_deps += [ ":$_copy_system_library_target_name" ]
+        _public_deps += [ ":$_copy_system_library_target_name" ]
       } else {
         _process_prebuilt_target_name = "${target_name}__process_prebuilt"
         process_java_prebuilt(_process_prebuilt_target_name) {
@@ -3557,7 +3553,11 @@
             java_sources_file = _java_sources_file
           }
           output_jar_path = _final_jar_path
-          deps = _java_full_deps + _accumulated_deps + _accumulated_public_deps
+          deps = _java_full_deps + _accumulated_deps +
+                 [ ":$_build_config_target_name" ]
+          if (_has_sources) {
+            deps += [ ":$_compile_java_target" ]
+          }
 
           # proguard_configs listed on java_library targets need to be marked
           # as inputs to at least one action so that "gn analyze" will know
@@ -3572,7 +3572,7 @@
             deps += _srcjar_deps  # For the aapt-generated proguard rules.
           }
         }
-        _accumulated_public_deps += [ ":$_process_prebuilt_target_name" ]
+        _public_deps += [ ":$_process_prebuilt_target_name" ]
 
         if (defined(_dex_path)) {
           dex("${target_name}__dex") {
@@ -3589,7 +3589,7 @@
             enable_multidex = false
             enable_library_multidex = true
           }
-          _accumulated_public_deps += [ ":${target_name}__dex" ]
+          _public_deps += [ ":${target_name}__dex" ]
         }
       }
     }
@@ -3608,9 +3608,9 @@
         if (defined(invoker.wrapper_script_name)) {
           script_name = invoker.wrapper_script_name
         }
-        deps = _accumulated_public_deps
+        deps = [ ":$_build_config_target_name" ]
       }
-      _accumulated_public_deps += [ ":${target_name}__java_binary_script" ]
+      _public_deps += [ ":${target_name}__java_binary_script" ]
     }
 
     group(target_name) {
@@ -3622,7 +3622,7 @@
                                "data_deps",
                                "visibility",
                              ])
-      public_deps = _accumulated_public_deps
+      public_deps = _public_deps
       if (defined(_analysis_public_deps)) {
         if (!defined(data_deps)) {
           data_deps = []
diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn
index 6c50e93..abd13a7a 100644
--- a/build/config/compiler/BUILD.gn
+++ b/build/config/compiler/BUILD.gn
@@ -1591,7 +1591,8 @@
 
     # TODO(thakis): Enable this more often, https://crbug.com/346399
     # use_libfuzzer: https://crbug.com/1063180
-    if (!is_nacl && !use_libfuzzer && target_os != "ios") {
+    if (!is_nacl && !use_libfuzzer &&
+        !(is_ios && ios_app_bundle_id_prefix != "org.chromium")) {
       cflags += [ "-Wunreachable-code" ]
     }
 
diff --git a/cc/metrics/frame_sequence_metrics_unittest.cc b/cc/metrics/frame_sequence_metrics_unittest.cc
index 353c337..a946e82 100644
--- a/cc/metrics/frame_sequence_metrics_unittest.cc
+++ b/cc/metrics/frame_sequence_metrics_unittest.cc
@@ -10,20 +10,16 @@
 
 namespace cc {
 
-TEST(FrameSequenceMetricsTest, SlowerThread) {
-  base::HistogramTester histogram_tester;
-
+TEST(FrameSequenceMetricsTest, AggregatedThroughput) {
   FrameSequenceMetrics first(FrameSequenceTrackerType::kTouchScroll, nullptr);
-  first.impl_throughput().frames_expected = 200;
-  first.impl_throughput().frames_produced = 190;
-  first.main_throughput().frames_expected = 100;
-  first.main_throughput().frames_produced = 50;
+  first.impl_throughput().frames_expected = 200u;
+  first.impl_throughput().frames_produced = 190u;
+  first.main_throughput().frames_expected = 100u;
+  first.main_throughput().frames_produced = 50u;
 
-  // slower thread throughput is computed at ReportMetrics().
-  first.ReportMetrics();
-  std::string metric =
-      "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll";
-  EXPECT_EQ(histogram_tester.GetBucketCount(metric, 50), 1);
+  // The aggregated throughput is computed at ReportMetrics().
+  first.ComputeAggregatedThroughputForTesting();
+  EXPECT_EQ(first.aggregated_throughput().frames_expected, 200u);
 }
 
 TEST(FrameSequenceMetricsTest, MergeMetrics) {
@@ -67,8 +63,6 @@
       1u);
   histograms.ExpectTotalCount(
       "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 0u);
-  histograms.ExpectTotalCount(
-      "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll", 1u);
 
   // There should still be data left over for the main-thread.
   EXPECT_TRUE(first.HasDataLeftForReporting());
@@ -86,8 +80,6 @@
       2u);
   histograms.ExpectTotalCount(
       "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 1u);
-  histograms.ExpectTotalCount(
-      "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll", 2u);
   // All the metrics have now been reported. No data should be left over.
   EXPECT_FALSE(first.HasDataLeftForReporting());
 
@@ -104,8 +96,6 @@
       1u);
   histograms.ExpectTotalCount(
       "Graphics.Smoothness.PercentDroppedFrames.MainThread.Universal", 1u);
-  histograms.ExpectTotalCount(
-      "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.Universal", 1u);
 }
 
 TEST(FrameSequenceMetricsTest, IrrelevantMetricsNotReported) {
diff --git a/cc/metrics/frame_sequence_tracker.cc b/cc/metrics/frame_sequence_tracker.cc
index 5dc64f7..0add814 100644
--- a/cc/metrics/frame_sequence_tracker.cc
+++ b/cc/metrics/frame_sequence_tracker.cc
@@ -143,8 +143,9 @@
 }
 
 FrameSequenceMetrics::~FrameSequenceMetrics() {
-  if (HasDataLeftForReporting())
+  if (HasDataLeftForReporting()) {
     ReportMetrics();
+  }
 }
 
 void FrameSequenceMetrics::SetScrollingThread(ThreadType scrolling_thread) {
@@ -160,12 +161,14 @@
   DCHECK_EQ(type_, metrics->type_);
   impl_throughput_.Merge(metrics->impl_throughput_);
   main_throughput_.Merge(metrics->main_throughput_);
+  aggregated_throughput_.Merge(metrics->aggregated_throughput_);
   frames_checkerboarded_ += metrics->frames_checkerboarded_;
 
   // Reset the state of |metrics| before destroying it, so that it doesn't end
   // up reporting the metrics.
   metrics->impl_throughput_ = {};
   metrics->main_throughput_ = {};
+  metrics->aggregated_throughput_ = {};
   metrics->frames_checkerboarded_ = 0;
 }
 
@@ -179,6 +182,17 @@
          main_throughput_.frames_expected > 0;
 }
 
+void FrameSequenceMetrics::ComputeAggregatedThroughput() {
+  // Whenever we are expecting and producing main frames, we are expecting and
+  // producing impl frames as well. As an example, if we expect one main frame
+  // to be produced, and when that main frame is presented, we are expecting 3
+  // impl frames, then the number of expected frames is 3 for the aggregated
+  // throughput.
+  aggregated_throughput_.frames_expected = impl_throughput_.frames_expected;
+  DCHECK_LE(aggregated_throughput_.frames_produced,
+            aggregated_throughput_.frames_produced);
+}
+
 void FrameSequenceMetrics::ReportMetrics() {
   DCHECK_LE(impl_throughput_.frames_produced, impl_throughput_.frames_expected);
   DCHECK_LE(main_throughput_.frames_produced, main_throughput_.frames_expected);
@@ -187,6 +201,8 @@
       ThroughputData::ToTracedValue(impl_throughput_, main_throughput_),
       "checkerboard", frames_checkerboarded_);
 
+  ComputeAggregatedThroughput();
+
   // Report the throughput metrics.
   base::Optional<int> impl_throughput_percent = ThroughputData::ReportHistogram(
       throughput_ukm_reporter_, type_, ThreadType::kCompositor,
@@ -200,34 +216,15 @@
   // Report for the 'slower thread' for the metrics where it makes sense.
   bool should_report_slower_thread =
       IsInteractionType(type_) || type_ == FrameSequenceTrackerType::kUniversal;
+  base::Optional<int> aggregated_throughput_percent;
   if (should_report_slower_thread) {
-    base::Optional<ThroughputData> slower_throughput;
-    base::Optional<int> slower_throughput_percent;
-    // The value is percent of dropped frames, slower throughput should have
-    // larger value.
-    if (impl_throughput_percent &&
-        (!main_throughput_percent ||
-         impl_throughput_percent.value() >= main_throughput_percent.value())) {
-      slower_throughput = impl_throughput_;
-    }
-    if (main_throughput_percent &&
-        (!impl_throughput_percent ||
-         main_throughput_percent.value() > impl_throughput_percent.value())) {
-      slower_throughput = main_throughput_;
-    }
-    if (slower_throughput.has_value()) {
-      slower_throughput_percent = ThroughputData::ReportHistogram(
-          throughput_ukm_reporter_, type_, ThreadType::kSlower,
-          GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_),
-          slower_throughput.value());
-      DCHECK(slower_throughput_percent.has_value())
-          << FrameSequenceTracker::GetFrameSequenceTrackerTypeName(type_);
-    }
-
-    // slower_throughput has value indicates that we have reported UMA.
-    if (slower_throughput.has_value() && throughput_ukm_reporter_) {
+    aggregated_throughput_percent = ThroughputData::ReportHistogram(
+        throughput_ukm_reporter_, type_, ThreadType::kSlower,
+        GetIndexForMetric(FrameSequenceMetrics::ThreadType::kSlower, type_),
+        aggregated_throughput_);
+    if (aggregated_throughput_percent.has_value() && throughput_ukm_reporter_) {
       throughput_ukm_reporter_->ReportThroughputUkm(
-          slower_throughput_percent, impl_throughput_percent,
+          aggregated_throughput_percent, impl_throughput_percent,
           main_throughput_percent, type_);
     }
   }
@@ -290,6 +287,8 @@
     impl_throughput_ = {};
   if (main_throughput_.frames_expected >= kMinFramesForThroughputMetric)
     main_throughput_ = {};
+  if (aggregated_throughput_percent.has_value())
+    aggregated_throughput_ = {};
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -700,16 +699,26 @@
       last_no_main_damage_sequence_ != 0 &&
       origin_args.frame_id.sequence_number == last_no_main_damage_sequence_;
 
-  if (!ShouldIgnoreBeginFrameSource(origin_args.frame_id.source_id) &&
-      main_changes_after_sequence_started && main_changes_include_new_changes &&
-      !main_change_had_no_damage) {
-    submitted_frame_had_new_main_content_ = true;
-    TRACKER_TRACE_STREAM << "S(" << origin_args.frame_id.sequence_number << ")";
+  if (!ShouldIgnoreBeginFrameSource(origin_args.frame_id.source_id)) {
+    if (main_changes_after_sequence_started &&
+        main_changes_include_new_changes && !main_change_had_no_damage) {
+      submitted_frame_had_new_main_content_ = true;
+      TRACKER_TRACE_STREAM << "S(" << origin_args.frame_id.sequence_number
+                           << ")";
 
-    last_submitted_main_sequence_ = origin_args.frame_id.sequence_number;
-    main_frames_.push_back(frame_token);
-    DCHECK_GE(main_throughput().frames_expected, main_frames_.size())
-        << TRACKER_DCHECK_MSG;
+      last_submitted_main_sequence_ = origin_args.frame_id.sequence_number;
+      main_frames_.push_back(frame_token);
+      DCHECK_GE(main_throughput().frames_expected, main_frames_.size())
+          << TRACKER_DCHECK_MSG;
+    } else {
+      // If we have sent a BeginMainFrame which hasn't yet been submitted, or
+      // confirmed that it has no damage (previous_sequence is set to 0), then
+      // we are currently expecting a main frame.
+      const bool expecting_main = begin_main_frame_data_.previous_sequence >
+                                  last_submitted_main_sequence_;
+      if (expecting_main)
+        expecting_main_when_submit_impl_.push_back(frame_token);
+    }
   }
 
   if (has_missing_content) {
@@ -816,6 +825,8 @@
   if (ignored_frame_tokens_.contains(frame_token))
     return;
 
+  uint32_t impl_frames_produced = 0;
+  uint32_t main_frames_produced = 0;
   trace_data_.Advance(feedback.timestamp);
 
   const bool was_presented = !feedback.timestamp.is_null();
@@ -824,6 +835,7 @@
               impl_throughput().frames_expected)
         << TRACKER_DCHECK_MSG;
     ++impl_throughput().frames_produced;
+    ++impl_frames_produced;
 
     if (frame_token_acks_last_frame)
       last_submitted_frame_ = 0;
@@ -842,6 +854,41 @@
                 main_throughput().frames_expected)
           << TRACKER_DCHECK_MSG;
       ++main_throughput().frames_produced;
+      ++main_frames_produced;
+    }
+
+    if (impl_frames_produced > 0) {
+      // If there is no main frame presented, then we need to see whether or not
+      // we are expecting main frames to be presented or not.
+      if (main_frames_produced == 0) {
+        // Only need to check the first element in the deque because the
+        // elements are in order.
+        bool expecting_main_frames =
+            !expecting_main_when_submit_impl_.empty() &&
+            !viz::FrameTokenGT(expecting_main_when_submit_impl_[0],
+                               frame_token);
+        if (expecting_main_frames) {
+          // We are expecting a main frame to be processed, the main frame
+          // should either report no-damage or be submitted to GPU. Since we
+          // don't know which case it would be, we accumulate the number of impl
+          // frames produced so that we can apply that to aggregated throughput
+          // if the main frame reports no-damage later on.
+          impl_frames_produced_while_expecting_main_ += impl_frames_produced;
+        } else {
+          DCHECK_EQ(impl_frames_produced_while_expecting_main_, 0u)
+              << TRACKER_DCHECK_MSG;
+          aggregated_throughput().frames_produced += impl_frames_produced;
+          impl_frames_produced_while_expecting_main_ = 0;
+        }
+      } else {
+        aggregated_throughput().frames_produced += main_frames_produced;
+        impl_frames_produced_while_expecting_main_ = 0;
+        while (!expecting_main_when_submit_impl_.empty() &&
+               !viz::FrameTokenGT(expecting_main_when_submit_impl_.front(),
+                                  frame_token)) {
+          expecting_main_when_submit_impl_.pop_front();
+        }
+      }
     }
 
     if (checkerboarding_.last_frame_had_checkerboarding) {
@@ -950,6 +997,11 @@
         << TRACKER_DCHECK_MSG;
   }
   begin_main_frame_data_.previous_sequence = 0;
+
+  aggregated_throughput().frames_produced +=
+      impl_frames_produced_while_expecting_main_;
+  impl_frames_produced_while_expecting_main_ = 0;
+  expecting_main_when_submit_impl_.clear();
 }
 
 void FrameSequenceTracker::PauseFrameProduction() {
diff --git a/cc/metrics/frame_sequence_tracker.h b/cc/metrics/frame_sequence_tracker.h
index d3d9669..1aee27f6 100644
--- a/cc/metrics/frame_sequence_tracker.h
+++ b/cc/metrics/frame_sequence_tracker.h
@@ -112,15 +112,20 @@
   bool HasDataLeftForReporting() const;
   // Report related metrics: throughput, checkboarding...
   void ReportMetrics();
+  void ComputeAggregatedThroughputForTesting() {
+    ComputeAggregatedThroughput();
+  }
 
   ThroughputData& impl_throughput() { return impl_throughput_; }
   ThroughputData& main_throughput() { return main_throughput_; }
+  ThroughputData& aggregated_throughput() { return aggregated_throughput_; }
   void add_checkerboarded_frames(int64_t frames) {
     frames_checkerboarded_ += frames;
   }
   uint32_t frames_checkerboarded() const { return frames_checkerboarded_; }
 
  private:
+  void ComputeAggregatedThroughput();
   const FrameSequenceTrackerType type_;
 
   // Pointer to the reporter owned by the FrameSequenceTrackerCollection.
@@ -128,6 +133,8 @@
 
   ThroughputData impl_throughput_;
   ThroughputData main_throughput_;
+  // The aggregated throughput for the main/compositor thread.
+  ThroughputData aggregated_throughput_;
 
   ThreadType scrolling_thread_ = ThreadType::kUnknown;
 
@@ -305,6 +312,9 @@
   FrameSequenceMetrics::ThroughputData& main_throughput() {
     return metrics_->main_throughput();
   }
+  FrameSequenceMetrics::ThroughputData& aggregated_throughput() {
+    return metrics_->aggregated_throughput();
+  }
 
   void ScheduleTerminate();
 
@@ -416,6 +426,14 @@
   uint64_t last_processed_main_sequence_ = 0;
   uint64_t last_processed_main_sequence_latency_ = 0;
 
+  // Used to compute aggregated throughput.
+  // When expecting a main frame, we accumulate the number of impl frames
+  // presented because if that main frame ends up with no-damage, then we should
+  // count the impl frames that were produced in the meantime.
+  uint32_t impl_frames_produced_while_expecting_main_ = 0;
+  // Each entry is a frame token, inserted at ReportSubmitFrame.
+  base::circular_deque<uint32_t> expecting_main_when_submit_impl_;
+
   // Handle off-screen main damage case. In this case, the sequence is typically
   // like: b(1)B(0,1)E(1)n(1)e(1)b(2)n(2)e(2)...b(10)E(2)B(10,10)n(10)e(10).
   // Note that between two 'E's, all the impl frames caused no damage, and
diff --git a/cc/metrics/frame_sequence_tracker_unittest.cc b/cc/metrics/frame_sequence_tracker_unittest.cc
index f9ba723..44bed000 100644
--- a/cc/metrics/frame_sequence_tracker_unittest.cc
+++ b/cc/metrics/frame_sequence_tracker_unittest.cc
@@ -144,9 +144,6 @@
         1u);
     histogram_tester.ExpectTotalCount(
         "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 0u);
-    histogram_tester.ExpectTotalCount(
-        "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll",
-        1u);
 
     // Test that both are reported.
     tracker_->impl_throughput().frames_expected = 100u;
@@ -159,9 +156,6 @@
         2u);
     histogram_tester.ExpectTotalCount(
         "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 1u);
-    histogram_tester.ExpectTotalCount(
-        "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll",
-        2u);
 
     // Test that none is reported.
     tracker_->main_throughput().frames_expected = 2u;
@@ -174,9 +168,6 @@
         2u);
     histogram_tester.ExpectTotalCount(
         "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 1u);
-    histogram_tester.ExpectTotalCount(
-        "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll",
-        2u);
 
     // Test the case where compositor and main thread have the same throughput.
     tracker_->impl_throughput().frames_expected = 120u;
@@ -189,9 +180,6 @@
         3u);
     histogram_tester.ExpectTotalCount(
         "Graphics.Smoothness.PercentDroppedFrames.MainThread.TouchScroll", 2u);
-    histogram_tester.ExpectTotalCount(
-        "Graphics.Smoothness.PercentDroppedFrames.SlowerThread.TouchScroll",
-        3u);
   }
 
   void GenerateSequence(const char* str) {
@@ -339,6 +327,9 @@
   FrameSequenceMetrics::ThroughputData& MainThroughput() const {
     return tracker_->main_throughput();
   }
+  FrameSequenceMetrics::ThroughputData& AggregatedThroughput() const {
+    return tracker_->aggregated_throughput();
+  }
 
   void SetTerminationStatus(FrameSequenceTracker::TerminationStatus status) {
     tracker_->termination_status_ = status;
@@ -785,6 +776,269 @@
   EXPECT_EQ(ImplThroughput().frames_produced, 1u);
 }
 
+// The following tests is for aggregating the compositor and main thread
+// throughput. There are a few categories and each category have some tests.
+// First category: no main frame involved.
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput1) {
+  const char sequence[] = "b(1)s(1)e(1,0)P(1)";
+  GenerateSequence(sequence);
+  // The test doesn't call TakeMetrics, so using the frames_expected in the
+  // ImplThroughput() to test the aggregated throughput.
+  EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+// Second category: one main frame and it responds in time.
+// Main frame is submitted.
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput2) {
+  const char sequence[] = "b(1)B(0,1)E(1)s(1)S(1)e(1,1)P(1)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+// Main frame has no damage.
+// variation: N(2,2) could be before s(1) or after.
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput3) {
+  const char sequence[] = "b(2)B(0,2)N(2,2)s(1)S(1)e(2,0)P(1)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput4) {
+  const char sequence[] = "b(2)B(0,2)s(1)S(1)N(2,2)e(2,0)P(1)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput5) {
+  const char sequence[] = "b(2)B(0,2)s(1)S(1)e(2,0)P(1)N(2,2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+// Third category: one main frame and responds slowly.
+// variation: the main frame could either report no-damage or submitted. The
+// presentation of the first frame could arrive at different time.
+// Main frame is submitted, P(1) could arrive at different time.
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput6) {
+  // At ReportSubmitFrame, |main_changes_after_sequence_start| is false at
+  // s(1)S(1).
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)b(3)E(1)s(2)S(2)e(3,1)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput7) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)P(1)b(3)E(1)s(2)S(2)e(3,1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+// The main frame eventually reports no-damage.
+// variation: P(1) arrives at different time, N(1,1) arrives before or after
+// s(2).
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput8) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)b(3)s(2)S(1)N(2,2)e(3,0)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput9) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)b(3)N(2,2)s(2)S(1)e(3,0)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput10) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)P(1)b(3)N(2,2)s(2)S(1)e(3,0)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput11) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)b(3)N(2,2)P(1)s(2)S(1)e(3,0)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput12) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)P(1)b(3)s(2)S(1)N(2,2)e(3,0)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput13) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)e(2,0)b(3)s(2)S(1)P(1)N(2,2)e(3,0)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+// Fourth category: one main frame responds in time, but presentation comes
+// late.
+// variation: the main frame could be submitted or no damage.
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput14) {
+  const char sequence[] =
+      "b(1)B(0,1)E(1)s(1)S(1)e(1,1)b(2)s(2)S(1)e(2,1)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput15) {
+  const char sequence[] =
+      "b(2)B(0,2)N(2,2)s(1)S(1)e(2,0)b(3)s(2)S(1)e(3,0)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput16) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)N(2,2)e(2,0)b(3)s(2)S(1)e(3,0)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+// Fifth category: two main frames, both responds in time.
+// variation: it is enough that one main frame is submitted, or both are. The
+// presentation of the first frame could arrive at different time.
+// Both main frames are submitted, P(1) could arrive at different time.
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput17) {
+  const char sequence[] =
+      "b(1)B(0,1)E(1)s(1)S(1)e(1,1)P(1)b(2)B(1,2)E(2)s(2)S(2)e(2,2)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput18) {
+  const char sequence[] =
+      "b(1)B(0,1)E(1)s(1)S(1)e(1,1)b(2)B(1,2)E(2)s(2)S(2)e(2,2)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+// The second main frame has no damage.
+// variation: N could come before or after s(2), and P(1) could arrive at
+// different time.
+// N after s(2).
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput19) {
+  const char sequence[] =
+      "b(2)B(0,2)E(2)s(1)S(2)e(2,2)b(3)B(2,3)s(2)S(1)N(3,3)e(3,2)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput20) {
+  const char sequence[] =
+      "b(2)B(0,2)E(2)s(1)S(2)e(2,2)P(1)b(3)B(2,3)s(2)S(1)N(3,3)e(3,2)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+// N before s(2).
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput21) {
+  const char sequence[] =
+      "b(2)B(0,2)E(2)s(1)S(2)e(2,2)b(3)B(2,3)N(3,3)s(2)S(1)e(3,2)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput22) {
+  const char sequence[] =
+      "b(2)B(0,2)E(2)s(1)S(2)e(2,2)P(1)b(3)B(2,3)N(3,3)s(2)S(1)e(3,2)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+// First main frame no damage.
+// N before s(1).
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput23) {
+  const char sequence[] =
+      "b(2)B(0,2)N(2,2)s(1)S(1)e(2,0)P(1)b(3)B(0,3)E(3)s(2)S(3)e(3,3)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput24) {
+  const char sequence[] =
+      "b(2)B(0,2)N(2,2)s(1)S(1)e(2,0)b(3)B(0,3)E(3)s(2)S(3)e(3,3)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+// N after s(1).
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput25) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)N(2,2)e(2,0)P(1)b(3)B(0,3)E(3)s(2)S(3)e(3,3)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput26) {
+  const char sequence[] =
+      "b(2)B(0,2)s(1)S(1)N(2,2)e(2,0)b(3)B(0,3)E(3)s(2)S(3)e(3,3)P(1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 2u);
+}
+
+// Following tests comes from test cases on the bots.
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput27) {
+  const char sequence[] = "b(2)B(0,2)s(1)S(1)e(2,0)E(2)P(1)b(3)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 0u);
+}
+
+// At ReportSubmitFrame, |main_changes_include_new_changes| is false at
+// s(2)S(1).
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput28) {
+  const char sequence[] =
+      "b(1)B(0,1)E(1)s(1)S(1)e(1,1)P(1)b(2)B(1,2)s(2)S(1)e(2,1)P(2)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 2u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 1u);
+}
+
+// At ReportSubmitFrame, |main_change_had_no_damage| is true at s(1)S(1).
+TEST_F(FrameSequenceTrackerTest, AggregatedThroughput29) {
+  const char sequence[] =
+      "b(1)B(0,1)n(1)N(1,1)e(1,0)b(2)B(0,2)s(1)S(1)e(2,0)P(1)";
+  GenerateSequence(sequence);
+  EXPECT_EQ(ImplThroughput().frames_expected, 1u);
+  EXPECT_EQ(AggregatedThroughput().frames_produced, 0u);
+}
+
 // b(2417)B(0,2417)E(2417)n(2417)N(2417,2417)
 TEST_F(FrameSequenceTrackerTest, SequenceNumberReset) {
   const char sequence[] =
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index a9a800e..5a4574b 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -170,13 +170,13 @@
     ":ui_locale_string_resources",
     "//chrome/android/webapk/libs/common:splash_resources",
     "//chrome/app:java_strings_grd",
-    "//chrome/browser/settings:java_resources",
     "//chrome/browser/ui/android/appmenu:java_resources",
     "//chrome/browser/ui/android/favicon:java_resources",
     "//chrome/browser/ui/android/strings:ui_strings_grd",
     "//chrome/browser/ui/messages/android:java_resources",
     "//components/autofill/android:autofill_java_resources",
     "//components/browser_ui/modaldialog/android:java_resources",
+    "//components/browser_ui/settings/android:java_resources",
     "//components/browser_ui/strings/android:browser_ui_strings_grd",
     "//components/browser_ui/styles/android:java_resources",
     "//components/browser_ui/widget/android:java_resources",
@@ -299,7 +299,6 @@
     "//chrome/browser/performance_hints/android:java",
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
-    "//chrome/browser/settings:chrome_managed_preference_delegate_java",
     "//chrome/browser/settings:java",
     "//chrome/browser/share/android:java_resources",
     "//chrome/browser/thumbnail:java",
@@ -317,6 +316,7 @@
     "//components/background_task_scheduler:background_task_scheduler_task_ids_java",
     "//components/bookmarks/common/android:bookmarks_java",
     "//components/browser_ui/modaldialog/android:java",
+    "//components/browser_ui/settings/android:java",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
@@ -843,7 +843,6 @@
     "//chrome/browser/performance_hints/android:java",
     "//chrome/browser/preferences:java",
     "//chrome/browser/profiles/android:java",
-    "//chrome/browser/settings:chrome_managed_preference_delegate_java",
     "//chrome/browser/settings:java",
     "//chrome/browser/thumbnail:java",
     "//chrome/browser/thumbnail:javatests",
@@ -862,6 +861,7 @@
     "//components/bookmarks/common/android:bookmarks_java",
     "//components/browser_ui/modaldialog/android:java",
     "//components/browser_ui/modaldialog/android:javatests",
+    "//components/browser_ui/settings/android:java",
     "//components/browser_ui/styles/android:java",
     "//components/browser_ui/util/android:java",
     "//components/browser_ui/widget/android:java",
diff --git a/chrome/android/chrome_java_sources.gni b/chrome/android/chrome_java_sources.gni
index a5417ad2..bcae627 100644
--- a/chrome/android/chrome_java_sources.gni
+++ b/chrome/android/chrome_java_sources.gni
@@ -1480,6 +1480,7 @@
   "java/src/org/chromium/chrome/browser/site_settings/AddExceptionPreference.java",
   "java/src/org/chromium/chrome/browser/site_settings/ChosenObjectInfo.java",
   "java/src/org/chromium/chrome/browser/site_settings/ChosenObjectSettings.java",
+  "java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsClient.java",
   "java/src/org/chromium/chrome/browser/site_settings/ClearWebsiteStorage.java",
   "java/src/org/chromium/chrome/browser/site_settings/ClearWebsiteStorageDialog.java",
   "java/src/org/chromium/chrome/browser/site_settings/ContentSetting.java",
@@ -1487,6 +1488,7 @@
   "java/src/org/chromium/chrome/browser/site_settings/ContentSettingsResources.java",
   "java/src/org/chromium/chrome/browser/site_settings/CookieControlsBridge.java",
   "java/src/org/chromium/chrome/browser/site_settings/CookieControlsServiceBridge.java",
+  "java/src/org/chromium/chrome/browser/site_settings/ForwardingManagedPreferenceDelegate.java",
   "java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java",
   "java/src/org/chromium/chrome/browser/site_settings/LocalStorageInfo.java",
   "java/src/org/chromium/chrome/browser/site_settings/LocationCategory.java",
@@ -1500,7 +1502,9 @@
   "java/src/org/chromium/chrome/browser/site_settings/SiteDataCleaner.java",
   "java/src/org/chromium/chrome/browser/site_settings/SiteSettings.java",
   "java/src/org/chromium/chrome/browser/site_settings/SiteSettingsCategory.java",
+  "java/src/org/chromium/chrome/browser/site_settings/SiteSettingsClient.java",
   "java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreference.java",
+  "java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreferenceFragment.java",
   "java/src/org/chromium/chrome/browser/site_settings/StorageInfo.java",
   "java/src/org/chromium/chrome/browser/site_settings/TriStateSiteSettingsPreference.java",
   "java/src/org/chromium/chrome/browser/site_settings/Website.java",
diff --git a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java
index 851cbf31..0646d57 100644
--- a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java
+++ b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/BLEHandler.java
@@ -513,9 +513,9 @@
      * Called by native code to process a makeCredential request.
      */
     @CalledByNative
-    void makeCredential(long client, byte[] clientDataHash, String rpId, byte[] userId,
+    void makeCredential(long client, String origin, String rpId, byte[] challenge, byte[] userId,
             int[] algorithms, byte[][] excludedCredentialIds, boolean residentKeyRequired) {
-        mAuthenticator.makeCredential(client, clientDataHash, rpId, userId, algorithms,
+        mAuthenticator.makeCredential(client, origin, rpId, challenge, userId, algorithms,
                 excludedCredentialIds, residentKeyRequired);
     }
 
@@ -524,10 +524,11 @@
      * makeCredential request.
      */
     public void onAuthenticatorAttestationResponse(
-            long client, int ctapStatus, byte[] attestationObject) {
-        mTaskRunner.postTask(()
-                                     -> BLEHandlerJni.get().onAuthenticatorAttestationResponse(
-                                             client, ctapStatus, attestationObject));
+            long client, int ctapStatus, byte[] clientDataJSON, byte[] attestationObject) {
+        mTaskRunner.postTask(
+                ()
+                        -> BLEHandlerJni.get().onAuthenticatorAttestationResponse(
+                                client, ctapStatus, clientDataJSON, attestationObject));
     }
 
     @NativeMethods
@@ -557,6 +558,6 @@
          * Called to alert native code of a response to a makeCredential request.
          */
         void onAuthenticatorAttestationResponse(
-                long client, int ctapStatus, byte[] attestationObject);
+                long client, int ctapStatus, byte[] clientDataJSON, byte[] attestationObject);
     }
 }
diff --git a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
index e90682eb..7ab2b9d 100644
--- a/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
+++ b/chrome/android/features/cablev2_authenticator/internal/java/src/org/chromium/chrome/browser/webauth/authenticator/CableAuthenticator.java
@@ -44,7 +44,6 @@
     private static final String TAG = "CableAuthenticator";
     private static final String FIDO2_KEY_CREDENTIAL_EXTRA = "FIDO2_CREDENTIAL_EXTRA";
     private static final int FIDO2_REGISTER_INTENT_CODE = 1;
-    private static final int CLIENT_DATA_HASH_LENGTH = 32;
 
     private static final int CTAP2_OK = 0;
     private static final int CTAP2_ERR_OPERATION_DENIED = 0x27;
@@ -66,12 +65,11 @@
         }
     }
 
-    public void makeCredential(long clientAddress, byte[] clientDataHash, String rpId,
+    public void makeCredential(long clientAddress, String origin, String rpId, byte[] challenge,
             byte[] userId, int[] algorithms, byte[][] excludedCredentialIds,
             boolean residentKeyRequired) {
         // TODO: handle concurrent requests
         assert mClientAddress == 0;
-        assert clientDataHash.length == CLIENT_DATA_HASH_LENGTH;
         mClientAddress = clientAddress;
         Fido2PrivilegedApiClient client = Fido.getFido2PrivilegedApiClient(mContext);
         if (client == null) {
@@ -103,7 +101,7 @@
                 new PublicKeyCredentialCreationOptions.Builder()
                         .setRp(new PublicKeyCredentialRpEntity(rpId, "", ""))
                         .setUser(new PublicKeyCredentialUserEntity(userId, "", null, ""))
-                        .setChallenge(clientDataHash)
+                        .setChallenge(challenge)
                         .setParameters(parameters)
                         .setTimeoutSeconds(Double.valueOf(20))
                         .setExcludeList(excludeCredentials)
@@ -113,7 +111,7 @@
         BrowserPublicKeyCredentialCreationOptions browserRequestOptions =
                 new BrowserPublicKeyCredentialCreationOptions.Builder()
                         .setPublicKeyCredentialCreationOptions(credentialCreationOptions)
-                        .setOrigin(Uri.parse("https://" + rpId))
+                        .setOrigin(Uri.parse(origin))
                         .build();
         Task<PendingIntent> result = client.getRegisterPendingIntent(browserRequestOptions);
         result.addOnSuccessListener(pedingIntent -> {
@@ -141,7 +139,7 @@
             Log.e(TAG, "Received a null intent.");
             // The user cancelled the request.
             mBleHandler.onAuthenticatorAttestationResponse(
-                    mClientAddress, CTAP2_ERR_OPERATION_DENIED, null);
+                    mClientAddress, CTAP2_ERR_OPERATION_DENIED, null, null);
             return;
         }
 
@@ -151,7 +149,7 @@
                             ? "CANCELED."
                             : "Failed with result code" + resultCode);
             mBleHandler.onAuthenticatorAttestationResponse(
-                    mClientAddress, CTAP2_ERR_OPERATION_DENIED, null);
+                    mClientAddress, CTAP2_ERR_OPERATION_DENIED, null, null);
             return;
         }
         Log.e(TAG, "OK.");
@@ -168,7 +166,8 @@
                                 + "and FIDO2_KEY_CREDENTIAL_EXTRA.");
             }
             // TODO: figure out what error to return here
-            mBleHandler.onAuthenticatorAttestationResponse(mClientAddress, CTAP2_ERR_OTHER, null);
+            mBleHandler.onAuthenticatorAttestationResponse(
+                    mClientAddress, CTAP2_ERR_OTHER, null, null);
             return;
         }
 
@@ -193,20 +192,22 @@
                     ctap_status = CTAP2_ERR_OTHER;
                     break;
             }
-            mBleHandler.onAuthenticatorAttestationResponse(mClientAddress, ctap_status, null);
+            mBleHandler.onAuthenticatorAttestationResponse(mClientAddress, ctap_status, null, null);
             return;
         }
         if (!(response instanceof AuthenticatorAttestationResponse)) {
             Log.i(TAG, "unknown response");
-            mBleHandler.onAuthenticatorAttestationResponse(mClientAddress, CTAP2_ERR_OTHER, null);
+            mBleHandler.onAuthenticatorAttestationResponse(
+                    mClientAddress, CTAP2_ERR_OTHER, null, null);
             return;
         }
 
         Log.i(TAG, "attestation response");
         AuthenticatorAttestationResponse attestation_response =
                 (AuthenticatorAttestationResponse) response;
-        mBleHandler.onAuthenticatorAttestationResponse(
-                mClientAddress, CTAP2_OK, attestation_response.getAttestationObject());
+        mBleHandler.onAuthenticatorAttestationResponse(mClientAddress, CTAP2_OK,
+                attestation_response.getClientDataJSON(),
+                attestation_response.getAttestationObject());
     }
 
     public void onQRCode(String value) {
diff --git a/chrome/android/features/cablev2_authenticator/internal/native/cablev2_authenticator_android.cc b/chrome/android/features/cablev2_authenticator/internal/native/cablev2_authenticator_android.cc
index 16b9419..7d47c85 100644
--- a/chrome/android/features/cablev2_authenticator/internal/native/cablev2_authenticator_android.cc
+++ b/chrome/android/features/cablev2_authenticator/internal/native/cablev2_authenticator_android.cc
@@ -9,6 +9,7 @@
 #include "base/numerics/safe_math.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/task/post_task.h"
+#include "components/cbor/diagnostic_writer.h"
 #include "components/cbor/reader.h"
 #include "components/cbor/writer.h"
 #include "components/device_event_log/device_event_log.h"
@@ -174,6 +175,7 @@
   const std::vector<uint8_t>* user_id;
   const cbor::Value::ArrayValue* cred_params;
   const cbor::Value::ArrayValue* excluded_credentials;
+  const cbor::Value* client_data_extension;
 };
 
 static constexpr StepOrByte<MakeCredRequest> kMakeCredParseSteps[] = {
@@ -198,6 +200,15 @@
     ELEMENT(Is::kOptional, MakeCredRequest, excluded_credentials),
     IntKey<MakeCredRequest>(5),
 
+    // TODO: remove once the FIDO API can handle clientDataJSON
+    Map<MakeCredRequest>(),
+    IntKey<MakeCredRequest>(6),
+      ELEMENT(Is::kRequired, MakeCredRequest, client_data_extension),
+      StringKey<MakeCredRequest>(),
+          'g', 'o', 'o', 'g', 'l', 'e', 'A', 'n', 'd', 'r', 'o', 'i', 'd',
+          'C', 'l', 'i', 'e', 'n', 't', 'D', 'a', 't', 'a', '\0',
+    Stop<MakeCredRequest>(),
+
     Stop<MakeCredRequest>(),
     // clang-format on
 };
@@ -230,8 +241,9 @@
     virtual ~Delegate() = default;
     virtual void OnMakeCredential(
         uint64_t client_addr,
-        base::span<const uint8_t> client_data_hash,
+        const std::string& origin,
         const std::string& rp_id,
+        base::span<const uint8_t> challenge,
         base::span<const uint8_t> user_id,
         base::span<const int> algorithms,
         std::vector<std::vector<uint8_t>> excluded_credential_ids,
@@ -378,6 +390,11 @@
                 << "CBOR decoding failed for " << base::HexEncode(cbor_bytes);
             return false;
           }
+          FIDO_LOG(DEBUG) << "<- (" << base::HexEncode(&command, 1) << ") "
+                          << cbor::DiagnosticWriter::Write(*payload);
+        } else {
+          FIDO_LOG(DEBUG) << "<- (" << base::HexEncode(&command, 1)
+                          << ") <no payload>";
         }
 
         switch (command) {
@@ -392,13 +409,15 @@
             std::array<uint8_t, device::kAaguidLength> aaguid{};
             std::vector<cbor::Value> versions;
             versions.emplace_back("FIDO_2_0");
-
+            std::vector<cbor::Value> extensions;
+            extensions.emplace_back(device::kExtensionAndroidClientData);
             // TODO: should be based on whether a screen-lock is enabled.
             cbor::Value::MapValue options;
             options.emplace("uv", true);
 
             cbor::Value::MapValue response_map;
             response_map.emplace(1, std::move(versions));
+            response_map.emplace(2, std::move(extensions));
             response_map.emplace(3, aaguid);
             response_map.emplace(4, std::move(options));
 
@@ -463,12 +482,23 @@
               return false;
             }
 
+            const cbor::Value::MapValue& client_data_map =
+                make_cred_request.client_data_extension->GetMap();
+            const auto origin_it = client_data_map.find(cbor::Value(2));
+            const auto challenge_it = client_data_map.find(cbor::Value(3));
+            if (origin_it == client_data_map.end() ||
+                !origin_it->second.is_string() ||
+                challenge_it == client_data_map.end() ||
+                !challenge_it->second.is_bytestring()) {
+              return false;
+            }
+
             // TODO: plumb the rk flag through once GmsCore supports resident
             // keys. This will require support for optional maps in |Extract|.
             delegate_->OnMakeCredential(
-                addr_, *make_cred_request.client_data_hash,
-                *make_cred_request.rp_id, *make_cred_request.user_id,
-                algorithms, excluded_credential_ids,
+                addr_, origin_it->second.GetString(), *make_cred_request.rp_id,
+                challenge_it->second.GetBytestring(),
+                *make_cred_request.user_id, algorithms, excluded_credential_ids,
                 /*resident_key=*/false);
             return true;
           }
@@ -666,17 +696,17 @@
 
   void OnMakeCredential(
       uint64_t client_addr,
-      base::span<const uint8_t> client_data_hash,
+      const std::string& origin,
       const std::string& rp_id,
+      base::span<const uint8_t> challenge,
       base::span<const uint8_t> user_id,
       base::span<const int> algorithms,
       std::vector<std::vector<uint8_t>> excluded_credential_ids,
       bool resident_key_required) override {
     // TODO: Add extension support if necessary.
     Java_BLEHandler_makeCredential(
-        env_, ble_handler_, client_addr,
-        ToJavaByteArray(env_, client_data_hash),
-        ConvertUTF8ToJavaString(env_, rp_id),
+        env_, ble_handler_, client_addr, ConvertUTF8ToJavaString(env_, origin),
+        ConvertUTF8ToJavaString(env_, rp_id), ToJavaByteArray(env_, challenge),
         // TODO: Pass full user entity once resident key support is added.
         ToJavaByteArray(env_, user_id), ToJavaIntArray(env_, algorithms),
         ToJavaArrayOfByteArray(env_, excluded_credential_ids),
@@ -685,6 +715,7 @@
 
   void OnMakeCredentialResponse(uint64_t client_addr,
                                 uint32_t ctap_status,
+                                base::span<const uint8_t> client_data_json,
                                 base::span<const uint8_t> attestation_object) {
     DCHECK_LE(ctap_status, 0xFFu);
     auto it = clients_.find(client_addr);
@@ -716,6 +747,8 @@
       response_map.emplace(
           2, base::span<const uint8_t>(*attestation_object.auth_data));
       response_map.emplace(3, attestation_object.statement->Clone());
+      response_map.emplace(device::kAndroidClientDataExtOutputKey,
+                           client_data_json);
 
       base::Optional<std::vector<uint8_t>> response_payload =
           cbor::Writer::Write(cbor::Value(std::move(response_map)));
@@ -889,11 +922,16 @@
     JNIEnv* env,
     jlong client,
     jint ctap_status,
+    const JavaParamRef<jbyteArray>& jclient_data_json,
     const JavaParamRef<jbyteArray>& jattestation_object) {
+  std::vector<uint8_t> client_data_json;
+  if (jattestation_object) {
+    JavaByteArrayToByteVector(env, jclient_data_json, &client_data_json);
+  }
   std::vector<uint8_t> attestation_object;
   if (jattestation_object) {
     JavaByteArrayToByteVector(env, jattestation_object, &attestation_object);
   }
   return CableInterface::GetInstance()->OnMakeCredentialResponse(
-      client, ctap_status, attestation_object);
+      client, ctap_status, client_data_json, attestation_object);
 }
diff --git a/chrome/android/java/DEPS b/chrome/android/java/DEPS
index b373a575..ef881be 100644
--- a/chrome/android/java/DEPS
+++ b/chrome/android/java/DEPS
@@ -21,6 +21,7 @@
   "+components/background_task_scheduler/android/java/src/org/chromium/components/background_task_scheduler",
   "+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks",
   "+components/browser_ui/widget/android",
+  "+components/browser_ui/settings/android",
   "+components/browser_ui/styles/android",
   "+components/crash/android/java",
   "+components/dom_distiller/content/browser/android/java/src/org/chromium/components/dom_distiller/content",
diff --git a/chrome/android/java/res/xml/accessibility_preferences.xml b/chrome/android/java/res/xml/accessibility_preferences.xml
index 54d6d560..68c16db 100644
--- a/chrome/android/java/res/xml/accessibility_preferences.xml
+++ b/chrome/android/java/res/xml/accessibility_preferences.xml
@@ -10,17 +10,17 @@
         android:title="@string/font_size"
         android:selectable="false" />
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="force_enable_zoom"
         android:summary="@string/force_enable_zoom_summary"
         android:title="@string/force_enable_zoom_title" />
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="reader_for_accessibility"
         android:summary="@string/reader_for_accessibility_summary"
         android:title="@string/reader_for_accessibility_title" />
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="accessibility_tab_switcher"
         android:summary="@string/accessibility_tab_switcher_summary"
         android:title="@string/accessibility_tab_switcher_title" />
diff --git a/chrome/android/java/res/xml/account_management_preferences.xml b/chrome/android/java/res/xml/account_management_preferences.xml
index fa74c034..6dc04a94 100644
--- a/chrome/android/java/res/xml/account_management_preferences.xml
+++ b/chrome/android/java/res/xml/account_management_preferences.xml
@@ -18,7 +18,7 @@
         android:key="parental_settings"
         android:title="@string/account_management_parental_settings"/>
 
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="parent_accounts"
         tools:summary="@string/account_management_two_parent_names"/>
 
diff --git a/chrome/android/java/res/xml/clear_browsing_data_preferences_tab.xml b/chrome/android/java/res/xml/clear_browsing_data_preferences_tab.xml
index 11e0cd64..9e9de91 100644
--- a/chrome/android/java/res/xml/clear_browsing_data_preferences_tab.xml
+++ b/chrome/android/java/res/xml/clear_browsing_data_preferences_tab.xml
@@ -7,7 +7,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.settings.SpinnerPreference
+    <org.chromium.components.browser_ui.settings.SpinnerPreference
         android:key="time_period_spinner"
         android:persistent="false"
         android:title="@string/clear_browsing_data_tab_period_title"
diff --git a/chrome/android/java/res/xml/contextual_search_preferences.xml b/chrome/android/java/res/xml/contextual_search_preferences.xml
index 5677d29..9f7a141 100644
--- a/chrome/android/java/res/xml/contextual_search_preferences.xml
+++ b/chrome/android/java/res/xml/contextual_search_preferences.xml
@@ -7,12 +7,12 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="contextual_search_switch"
         android:summaryOn="@string/text_on"
         android:summaryOff="@string/text_off" />
 
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:title="@string/contextual_search_description"
         app:allowDividerBelow="false" />
 
diff --git a/chrome/android/java/res/xml/contextual_search_tap_preferences.xml b/chrome/android/java/res/xml/contextual_search_tap_preferences.xml
index 1dcffbf..4e9a139 100644
--- a/chrome/android/java/res/xml/contextual_search_tap_preferences.xml
+++ b/chrome/android/java/res/xml/contextual_search_tap_preferences.xml
@@ -7,12 +7,12 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="contextual_search_switch"
         android:summaryOn="@string/text_on"
         android:summaryOff="@string/text_off" />
 
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:title="@string/contextual_search_tap_description"
         app:allowDividerBelow="false" />
 
diff --git a/chrome/android/java/res/xml/data_reduction_preferences_off_lite_mode.xml b/chrome/android/java/res/xml/data_reduction_preferences_off_lite_mode.xml
index 09d4379..d0222950 100644
--- a/chrome/android/java/res/xml/data_reduction_preferences_off_lite_mode.xml
+++ b/chrome/android/java/res/xml/data_reduction_preferences_off_lite_mode.xml
@@ -6,11 +6,11 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   xmlns:app="http://schemas.android.com/apk/res-auto" >
 
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:title="@string/data_reduction_benefits_description_lite_mode"
         app:allowDividerBelow="false" />
 
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:title="@string/data_reduction_description_lite_mode"
         app:allowDividerBelow="false" />
 
diff --git a/chrome/android/java/res/xml/developer_preferences.xml b/chrome/android/java/res/xml/developer_preferences.xml
index 6746189b..5c24563 100644
--- a/chrome/android/java/res/xml/developer_preferences.xml
+++ b/chrome/android/java/res/xml/developer_preferences.xml
@@ -10,7 +10,7 @@
         android:fragment="org.chromium.chrome.browser.tracing.settings.TracingSettings"
         android:key="tracing"
         android:title="Tracing" />
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:enabled="false"
diff --git a/chrome/android/java/res/xml/do_not_track_preferences.xml b/chrome/android/java/res/xml/do_not_track_preferences.xml
index a5d99fb..ca94952 100644
--- a/chrome/android/java/res/xml/do_not_track_preferences.xml
+++ b/chrome/android/java/res/xml/do_not_track_preferences.xml
@@ -7,12 +7,12 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="do_not_track_switch"
         android:summaryOn="@string/text_on"
         android:summaryOff="@string/text_off" />
 
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:title="@string/do_not_track_description"
         app:allowDividerBelow="false" />
 
diff --git a/chrome/android/java/res/xml/download_preferences.xml b/chrome/android/java/res/xml/download_preferences.xml
index a721add..1b8bd2d 100644
--- a/chrome/android/java/res/xml/download_preferences.xml
+++ b/chrome/android/java/res/xml/download_preferences.xml
@@ -10,13 +10,13 @@
         android:positiveButtonText="@string/done"
         android:negativeButtonText="@null"  />
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="location_prompt_enabled"
         android:title="@string/download_location_prompt_enabled_title"
         android:summaryOn="@string/text_on"
         android:summaryOff="@string/text_off" />
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="prefetching_enabled"
         android:title="@string/download_settings_enable_prefetch_title" />
 </PreferenceScreen>
diff --git a/chrome/android/java/res/xml/homepage_preferences.xml b/chrome/android/java/res/xml/homepage_preferences.xml
index 14f411fc..312b82dd0 100644
--- a/chrome/android/java/res/xml/homepage_preferences.xml
+++ b/chrome/android/java/res/xml/homepage_preferences.xml
@@ -7,7 +7,7 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="homepage_switch"
         android:summaryOn="@string/text_on"
         android:summaryOff="@string/text_off" />
@@ -25,7 +25,7 @@
         app:allowDividerAbove="true"
         app:allowDividerBelow="false" />
 
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="text_managed"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/xml/languages_preferences.xml b/chrome/android/java/res/xml/languages_preferences.xml
index 550563e..e2499725 100644
--- a/chrome/android/java/res/xml/languages_preferences.xml
+++ b/chrome/android/java/res/xml/languages_preferences.xml
@@ -11,7 +11,7 @@
         android:layout="@layout/languages_preference"
         android:widgetLayout="@layout/accept_languages_list" />
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="translate_switch"
         android:summaryOn="@string/languages_offer_translate_switch"
         android:summaryOff="@string/languages_offer_translate_switch" />
diff --git a/chrome/android/java/res/xml/legal_information_preferences.xml b/chrome/android/java/res/xml/legal_information_preferences.xml
index 2eca6ef..9ecd4175 100644
--- a/chrome/android/java/res/xml/legal_information_preferences.xml
+++ b/chrome/android/java/res/xml/legal_information_preferences.xml
@@ -11,9 +11,13 @@
         android:title="@string/open_source_license_title"
         app:url="@string/open_source_license_url" />
     <org.chromium.chrome.browser.settings.HyperlinkPreference
-        android:key="terms_of_service"
-        android:title="@string/terms_of_service_title"
-        app:url="@string/chrome_terms_of_service_url" />
+        android:key="google_terms_of_service"
+        android:title="@string/google_terms_of_service_title"
+        app:url="@string/google_terms_of_service_url" />
+    <org.chromium.chrome.browser.settings.HyperlinkPreference
+        android:key="chrome_additional_terms_of_service"
+        android:title="@string/chrome_additional_terms_of_service_title"
+        app:url="@string/chrome_additional_terms_of_service_url" />
     <org.chromium.chrome.browser.settings.HyperlinkPreference
         android:key="privacy_notice"
         android:title="@string/privacy_notice_title"
diff --git a/chrome/android/java/res/xml/main_preferences.xml b/chrome/android/java/res/xml/main_preferences.xml
index 859bbf2..d4d4626a 100644
--- a/chrome/android/java/res/xml/main_preferences.xml
+++ b/chrome/android/java/res/xml/main_preferences.xml
@@ -14,7 +14,7 @@
         android:key="sign_in"
         android:order="1"
         android:title="@string/sign_in_to_chrome"/>
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="sync_and_services"
         android:order="2"
         android:layout="@layout/account_management_account_row"
@@ -25,22 +25,22 @@
         android:key="basics_section"
         android:order="3"
         android:title="@string/prefs_section_basics"/>
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.chrome.browser.search_engines.settings.SearchEngineSettings"
         android:key="search_engine"
         android:order="4"
         android:title="@string/search_engine_settings"/>
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.chrome.browser.password_manager.settings.PasswordSettings"
         android:key="passwords"
         android:order="5"
         android:title="@string/password_settings_title"/>
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.chrome.browser.autofill.settings.AutofillPaymentMethodsFragment"
         android:key="autofill_payment_methods"
         android:order="6"
         android:title="@string/autofill_payment_methods"/>
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.chrome.browser.autofill.settings.AutofillProfilesFragment"
         android:key="autofill_addresses"
         android:order="7"
@@ -85,12 +85,12 @@
         android:key="languages"
         android:order="15"
         android:title="@string/language_settings"/>
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.chrome.browser.datareduction.settings.DataReductionPreferenceFragment"
         android:key="data_reduction"
         android:order="16"
         android:title="@string/data_reduction_title_lite_mode"/>
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:fragment="org.chromium.chrome.browser.download.settings.DownloadSettings"
         android:key="downloads"
         android:order="17"
diff --git a/chrome/android/java/res/xml/manage_sync_preferences.xml b/chrome/android/java/res/xml/manage_sync_preferences.xml
index 4246742f..f36c3593 100644
--- a/chrome/android/java/res/xml/manage_sync_preferences.xml
+++ b/chrome/android/java/res/xml/manage_sync_preferences.xml
@@ -6,56 +6,56 @@
 <PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="sync_everything"
         android:title="@string/sync_everything_pref"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="sync_autofill"
         android:title="@string/sync_autofill"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="sync_bookmarks"
         android:title="@string/sync_bookmarks"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="sync_payments_integration"
         android:title="@string/sync_payments_integration"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="sync_history"
         android:title="@string/sync_history"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="sync_passwords"
         android:title="@string/sync_passwords"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="sync_recent_tabs"
         android:title="@string/sync_recent_tabs"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="sync_settings"
         android:title="@string/sync_settings"
         android:persistent="false"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="google_activity_controls"
         android:title="@string/sign_in_google_activity_controls_title"
         android:summary="@string/sign_in_google_activity_controls_summary"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="encryption"
         android:title="@string/sync_encryption"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="sync_manage_data"
         android:title="@string/sync_manage_data"/>
 
diff --git a/chrome/android/java/res/xml/notifications_preferences.xml b/chrome/android/java/res/xml/notifications_preferences.xml
index 8ab643a..7fa8f12 100644
--- a/chrome/android/java/res/xml/notifications_preferences.xml
+++ b/chrome/android/java/res/xml/notifications_preferences.xml
@@ -5,7 +5,7 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="content_suggestions"
         android:title="@string/notifications_content_suggestions_title"
         android:summary="@string/notifications_content_suggestions_summary" />
diff --git a/chrome/android/java/res/xml/privacy_preferences.xml b/chrome/android/java/res/xml/privacy_preferences.xml
index 27b29bc..9cef8b7c 100644
--- a/chrome/android/java/res/xml/privacy_preferences.xml
+++ b/chrome/android/java/res/xml/privacy_preferences.xml
@@ -6,16 +6,16 @@
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="can_make_payment"
         android:title="@string/can_make_payment_title"
         android:summary="@string/settings_can_make_payment_toggle_label" />
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="preload_pages"
         android:title="@string/preload_pages_title"
         android:summary="@string/preload_pages_summary"
         android:persistent="false" />
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="usage_stats_reporting"
         android:title="@string/usage_stats_setting_title"
         android:persistent="false" />
@@ -28,7 +28,7 @@
         android:title="@string/clear_browsing_data_title"
         android:summary="@string/clear_browsing_data_summary"
         android:fragment="org.chromium.chrome.browser.browsing_data.ClearBrowsingDataTabsFragment" />
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="sync_and_services_link"
         android:summary="@string/privacy_sync_and_services_link"
         app:allowDividerBelow="false" />
diff --git a/chrome/android/java/res/xml/single_website_preferences.xml b/chrome/android/java/res/xml/single_website_preferences.xml
index 534ce25f..21e09c7 100644
--- a/chrome/android/java/res/xml/single_website_preferences.xml
+++ b/chrome/android/java/res/xml/single_website_preferences.xml
@@ -7,14 +7,14 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="os_permissions_warning" />
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="os_permissions_warning_extra" />
     <Preference
         android:key="os_permissions_warning_divider"
         android:layout="@layout/divider_preference" />
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="intrusive_ads_info"
         android:title="@string/intrusive_ads_information"
         android:icon="@drawable/btn_info"
@@ -25,7 +25,7 @@
     <PreferenceCategory
         android:key="site_heading"
         android:title="@string/website_settings_site_category" />
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="site_title" />
     <PreferenceCategory
         android:key="site_usage"
@@ -79,7 +79,7 @@
     <ListPreference
         android:key="ar_permission_list" />
 
-    <org.chromium.chrome.browser.settings.ButtonPreference
+    <org.chromium.components.browser_ui.settings.ButtonPreference
         android:key="reset_site_button"
         android:title="@string/website_reset" />
 </PreferenceScreen>
diff --git a/chrome/android/java/res/xml/sync_and_services_preferences.xml b/chrome/android/java/res/xml/sync_and_services_preferences.xml
index c5512260..86e073e 100644
--- a/chrome/android/java/res/xml/sync_and_services_preferences.xml
+++ b/chrome/android/java/res/xml/sync_and_services_preferences.xml
@@ -15,7 +15,7 @@
         android:key="sign_in"
         android:title="@string/sign_in_to_chrome"/>
 
-    <org.chromium.chrome.browser.settings.ChromeBasePreference
+    <org.chromium.components.browser_ui.settings.ChromeBasePreference
         android:key="manage_your_google_account"
         android:title="@string/manage_your_google_account"/>
 
@@ -30,12 +30,12 @@
             android:key="sync_disabled_by_administrator"
             android:layout="@layout/account_management_account_row"
             android:title="@string/sync_is_disabled_by_administrator"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="sync_requested"
             android:title="@string/sync_switch_title"
             android:persistent="false"/>
 
-        <org.chromium.chrome.browser.settings.ChromeBasePreference
+        <org.chromium.components.browser_ui.settings.ChromeBasePreference
             android:key="manage_sync"
             android:title="@string/manage_sync_title"
             android:fragment="org.chromium.chrome.browser.sync.settings.ManageSyncSettings"/>
@@ -45,46 +45,46 @@
         android:key="services_category"
         android:title="@string/services_category_title">
 
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="search_suggestions"
             android:title="@string/autocomplete_searches_and_urls_title"
             android:summary="@string/autocomplete_searches_and_urls_summary"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="navigation_error"
             android:title="@string/navigation_error_suggestions_title"
             android:summary="@string/navigation_error_suggestions_summary"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="safe_browsing"
             android:title="@string/safe_browsing_title"
             android:summary="@string/safe_browsing_summary"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="password_leak_detection"
             android:title="@string/passwords_leak_detection_switch_title"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="safe_browsing_scout_reporting"
             android:title="@string/safe_browsing_scout_reporting_title"
             android:summary="@string/safe_browsing_scout_reporting_summary"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="usage_and_crash_reports"
             android:title="@string/usage_and_crash_reports_title"
             android:summary="@string/usage_and_crash_reports_summary"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="url_keyed_anonymized_data"
             android:title="@string/url_keyed_anonymized_data_title"
             android:summary="@string/url_keyed_anonymized_data_summary"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+        <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
             android:key="autofill_assistant"
             android:title="@string/prefs_autofill_assistant_title"
             android:summary="@string/prefs_autofill_assistant_summary"
             android:persistent="false"/>
-        <org.chromium.chrome.browser.settings.ChromeBasePreference
+        <org.chromium.components.browser_ui.settings.ChromeBasePreference
             android:key="contextual_search"
             android:title="@string/contextual_search_title"
             android:fragment="org.chromium.chrome.browser.contextualsearch.ContextualSearchPreferenceFragment"/>
diff --git a/chrome/android/java/res/xml/tracing_preferences.xml b/chrome/android/java/res/xml/tracing_preferences.xml
index 85eba32a..0825ebc 100644
--- a/chrome/android/java/res/xml/tracing_preferences.xml
+++ b/chrome/android/java/res/xml/tracing_preferences.xml
@@ -18,9 +18,9 @@
         android:key="mode"
         android:title="Tracing mode"
         android:persistent="false"/>
-    <org.chromium.chrome.browser.settings.ButtonPreference
+    <org.chromium.components.browser_ui.settings.ButtonPreference
         android:key="start_recording"/>
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="tracing_status"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/chrome/android/java/res/xml/website_preferences.xml b/chrome/android/java/res/xml/website_preferences.xml
index e6b4b36..2364057 100644
--- a/chrome/android/java/res/xml/website_preferences.xml
+++ b/chrome/android/java/res/xml/website_preferences.xml
@@ -9,13 +9,13 @@
     android:title="@string/all_sites">
 
     <!-- A text message describing cookie settings. -->
-    <org.chromium.chrome.browser.settings.TextMessagePreference
+    <org.chromium.components.browser_ui.settings.TextMessagePreference
         android:key="cookie_info_text"
         android:summary="@string/website_settings_cookie_info"/>
 
     <!-- A common binary toggle, only shown for specific categories that allow
          turning default values for that category on/off.-->
-    <org.chromium.chrome.browser.settings.ChromeSwitchPreference
+    <org.chromium.components.browser_ui.settings.ChromeSwitchPreference
         android:key="binary_toggle"
         android:defaultValue="true" />
     <!-- A common Allow/Ask/Block 3-state toggle (radio group). Only shown when
@@ -28,20 +28,20 @@
         android:key="four_state_cookie_toggle" />
 
     <!-- A toggle for blocking third-party cookies, only shown for the Cookies category. -->
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="third_party_cookies"
         android:title="@string/block_third_party_cookies_title"
         android:summary="@string/block_third_party_cookies_summary"
         android:defaultValue="true"
         android:persistent="false" />
     <!-- A checkbox for enabling a quiet ui for notification prompts. Only shown in the Notifications category. -->
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="notifications_quiet_ui"
         android:title="@string/website_settings_category_notifications_quiet"
         android:defaultValue="false"
         android:persistent="false" />
     <!-- A toggle for enabling vibration in notifications. -->
-    <org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference
+    <org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference
         android:key="notifications_vibrate"
         android:title="@string/enable_notifications_vibrate_title"
         android:summary="@string/enable_notifications_vibrate_summary"
@@ -53,10 +53,10 @@
         app:helpContext="@string/help_context_protected_content"/>
 
     <!-- Collapsible headers for sorting preferences. -->
-    <org.chromium.chrome.browser.settings.ExpandablePreferenceGroup
+    <org.chromium.components.browser_ui.settings.ExpandablePreferenceGroup
         android:key="managed_group" />
-    <org.chromium.chrome.browser.settings.ExpandablePreferenceGroup
+    <org.chromium.components.browser_ui.settings.ExpandablePreferenceGroup
         android:key="blocked_group" />
-    <org.chromium.chrome.browser.settings.ExpandablePreferenceGroup
+    <org.chromium.components.browser_ui.settings.ExpandablePreferenceGroup
         android:key="allowed_group" />
 </PreferenceScreen>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java
index 2ec63db..c12b88f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/AboutChromeSettings.java
@@ -15,8 +15,8 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeVersionInfo;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.tracing.settings.DeveloperSettings;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.ui.widget.Toast;
 
 import java.util.Calendar;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/LegalInformationSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/LegalInformationSettings.java
index e1ad1797..311d830 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/about_settings/LegalInformationSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/about_settings/LegalInformationSettings.java
@@ -9,7 +9,7 @@
 import androidx.preference.PreferenceFragmentCompat;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Fragment to display legal information about Chrome.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java
index ab40f6d4..0c4fd9a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettings.java
@@ -18,9 +18,9 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Fragment to keep track of all the accessibility related preferences.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
index 70ff445..1781cfb5 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/appmenu/AppMenuPropertiesDelegateImpl.java
@@ -39,7 +39,6 @@
 import org.chromium.chrome.browser.incognito.IncognitoUtils;
 import org.chromium.chrome.browser.multiwindow.MultiWindowModeStateDispatcher;
 import org.chromium.chrome.browser.omaha.UpdateMenuItemHelper;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
 import org.chromium.chrome.browser.share.ShareHelper;
 import org.chromium.chrome.browser.share.ShareUtils;
 import org.chromium.chrome.browser.tab.Tab;
@@ -52,6 +51,7 @@
 import org.chromium.chrome.browser.ui.appmenu.AppMenuPropertiesDelegate;
 import org.chromium.chrome.browser.ui.appmenu.CustomViewBinder;
 import org.chromium.chrome.features.start_surface.StartSurfaceConfiguration;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.ui.base.DeviceFormFactor;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
index 02cea1d3..0373fdf 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/prefeditor/EditorDialog.java
@@ -45,7 +45,7 @@
 import org.chromium.chrome.browser.autofill.settings.CreditCardNumberFormattingTextWatcher;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.widget.AlwaysDismissedDialog;
 import org.chromium.components.browser_ui.widget.FadingEdgeScrollView;
 import org.chromium.components.browser_ui.widget.TintedDrawable;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AndroidPaymentAppsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AndroidPaymentAppsFragment.java
index 178be1863..b4c1ed38 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AndroidPaymentAppsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AndroidPaymentAppsFragment.java
@@ -20,7 +20,7 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.payments.AndroidPaymentAppFactory;
 import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge;
-import org.chromium.chrome.browser.settings.TextMessagePreference;
+import org.chromium.components.browser_ui.settings.TextMessagePreference;
 
 import java.util.Map;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
index 8f08dbb..60f5f909 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillEditorBase.java
@@ -28,7 +28,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.prefeditor.EditorDialog;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.browser_ui.widget.FadingEdgeScrollView;
 
 /** Base class for Autofill editors (e.g. credit cards and profiles). */
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
index ffb956e..314e7b3 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillPaymentMethodsFragment.java
@@ -24,7 +24,7 @@
 import org.chromium.chrome.browser.payments.AndroidPaymentAppFactory;
 import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 
 /**
  * Autofill credit cards fragment, which allows the user to edit credit cards and control
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
index de631f0..b01096b 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillProfilesFragment.java
@@ -25,7 +25,7 @@
 import org.chromium.chrome.browser.payments.AutofillAddress;
 import org.chromium.chrome.browser.payments.SettingsAutofillAndPaymentsObserver;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 
 /**
  * Autofill profiles fragment, which allows the user to edit autofill profiles.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerProfileFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerProfileFragment.java
index a7fdf7d..56d353f7fc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerProfileFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/autofill/settings/AutofillServerProfileFragment.java
@@ -15,7 +15,7 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
 import org.chromium.chrome.browser.customtabs.CustomTabActivity;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Fragment for settings page that allows user to view and edit a single server-provided address.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java
index 423504c..8be477f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataCheckBoxPreference.java
@@ -18,8 +18,8 @@
 import androidx.preference.PreferenceViewHolder;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
 import org.chromium.chrome.browser.util.AccessibilityUtil;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java
index 2569ed7..8a49b80 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/browsing_data/ClearBrowsingDataFragment.java
@@ -33,8 +33,8 @@
 import org.chromium.chrome.browser.multiwindow.MultiWindowUtils;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.settings.SettingsUtils;
-import org.chromium.chrome.browser.settings.SpinnerPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SpinnerPreference;
 import org.chromium.ui.widget.ButtonCompat;
 
 import java.lang.annotation.Retention;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPreferenceFragment.java
index 3194d6e..31cfa8aa 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPreferenceFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/contextualsearch/ContextualSearchPreferenceFragment.java
@@ -12,8 +12,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Fragment to manage the Contextual Search preference and to explain to the user what it does.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java
index fe723db..fc53fd37f 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/datareduction/settings/DataReductionPreferenceFragment.java
@@ -25,9 +25,9 @@
 import org.chromium.chrome.browser.net.spdyproxy.DataReductionProxySettings.ContentLengths;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.util.ConversionUtils;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 import java.text.NumberFormat;
 import java.util.Locale;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java
index faeccca..3b288e20 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/download/settings/DownloadSettings.java
@@ -14,8 +14,8 @@
 import org.chromium.chrome.browser.download.DownloadPromptStatus;
 import org.chromium.chrome.browser.download.DownloadUtils;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Fragment containing Download settings.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java
index 076f3ff..b281775 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/LightweightFirstRunActivity.java
@@ -62,10 +62,10 @@
                                .inflate(R.layout.lightweight_fre_tos, null));
 
         final Resources resources = getResources();
-        NoUnderlineClickableSpan clickableTermsSpan = new NoUnderlineClickableSpan(
-                resources, (view) -> showInfoPage(R.string.chrome_terms_of_service_url));
-        NoUnderlineClickableSpan clickablePrivacySpan = new NoUnderlineClickableSpan(
-                resources, (view) -> showInfoPage(R.string.chrome_privacy_notice_url));
+        NoUnderlineClickableSpan clickableGoogleTermsSpan = new NoUnderlineClickableSpan(
+                resources, (view) -> showInfoPage(R.string.google_terms_of_service_url));
+        NoUnderlineClickableSpan clickableChromeAdditionalTermsSpan = new NoUnderlineClickableSpan(
+                resources, (view) -> showInfoPage(R.string.chrome_additional_terms_of_service_url));
         NoUnderlineClickableSpan clickableFamilyLinkPrivacySpan = new NoUnderlineClickableSpan(
                 resources, (view) -> showInfoPage(R.string.family_link_privacy_policy_url));
         String associatedAppName =
@@ -78,15 +78,14 @@
             tosAndPrivacyText = SpanApplier.applySpans(
                     getString(R.string.lightweight_fre_associated_app_tos_and_privacy_child_account,
                             associatedAppName),
-                    new SpanInfo("<LINK1>", "</LINK1>", clickableTermsSpan),
-                    new SpanInfo("<LINK2>", "</LINK2>", clickablePrivacySpan),
+                    new SpanInfo("<LINK1>", "</LINK1>", clickableGoogleTermsSpan),
+                    new SpanInfo("<LINK2>", "</LINK2>", clickableChromeAdditionalTermsSpan),
                     new SpanInfo("<LINK3>", "</LINK3>", clickableFamilyLinkPrivacySpan));
         } else {
             tosAndPrivacyText = SpanApplier.applySpans(
-                    getString(R.string.lightweight_fre_associated_app_tos_and_privacy,
-                            associatedAppName),
-                    new SpanInfo("<LINK1>", "</LINK1>", clickableTermsSpan),
-                    new SpanInfo("<LINK2>", "</LINK2>", clickablePrivacySpan));
+                    getString(R.string.lightweight_fre_associated_app_tos, associatedAppName),
+                    new SpanInfo("<LINK1>", "</LINK1>", clickableGoogleTermsSpan),
+                    new SpanInfo("<LINK2>", "</LINK2>", clickableChromeAdditionalTermsSpan));
         }
         TextView tosAndPrivacyTextView =
                 (TextView) findViewById(R.id.lightweight_fre_tos_and_privacy);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
index 181246b..150446bc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/firstrun/ToSAndUMAFirstRunFragment.java
@@ -93,41 +93,38 @@
         mTosAndPrivacy.setMovementMethod(LinkMovementMethod.getInstance());
 
         Resources resources = getResources();
-        NoUnderlineClickableSpan clickableTermsSpan =
+        NoUnderlineClickableSpan clickableGoogleTermsSpan =
                 new NoUnderlineClickableSpan(resources, (view1) -> {
                     if (!isAdded()) return;
-                    getPageDelegate().showInfoPage(R.string.chrome_terms_of_service_url);
+                    getPageDelegate().showInfoPage(R.string.google_terms_of_service_url);
                 });
-
-        NoUnderlineClickableSpan clickablePrivacySpan =
+        NoUnderlineClickableSpan clickableChromeAdditionalTermsSpan =
                 new NoUnderlineClickableSpan(resources, (view1) -> {
                     if (!isAdded()) return;
-                    getPageDelegate().showInfoPage(R.string.chrome_privacy_notice_url);
+                    getPageDelegate().showInfoPage(R.string.chrome_additional_terms_of_service_url);
                 });
-
         NoUnderlineClickableSpan clickableFamilyLinkPrivacySpan =
                 new NoUnderlineClickableSpan(resources, (view1) -> {
                     if (!isAdded()) return;
                     getPageDelegate().showInfoPage(R.string.family_link_privacy_policy_url);
                 });
 
-        final CharSequence tosAndPrivacyText;
+        final CharSequence tosText;
         Bundle freProperties = getPageDelegate().getProperties();
         @ChildAccountStatus.Status
         int childAccountStatus = freProperties.getInt(
                 SigninFirstRunFragment.CHILD_ACCOUNT_STATUS, ChildAccountStatus.NOT_CHILD);
         if (childAccountStatus == ChildAccountStatus.REGULAR_CHILD) {
-            tosAndPrivacyText =
-                    SpanApplier.applySpans(getString(R.string.fre_tos_and_privacy_child_account),
-                            new SpanInfo("<LINK1>", "</LINK1>", clickableTermsSpan),
-                            new SpanInfo("<LINK2>", "</LINK2>", clickablePrivacySpan),
-                            new SpanInfo("<LINK3>", "</LINK3>", clickableFamilyLinkPrivacySpan));
+            tosText = SpanApplier.applySpans(getString(R.string.fre_tos_and_privacy_child_account),
+                    new SpanInfo("<LINK1>", "</LINK1>", clickableGoogleTermsSpan),
+                    new SpanInfo("<LINK2>", "</LINK2>", clickableChromeAdditionalTermsSpan),
+                    new SpanInfo("<LINK3>", "</LINK3>", clickableFamilyLinkPrivacySpan));
         } else {
-            tosAndPrivacyText = SpanApplier.applySpans(getString(R.string.fre_tos_and_privacy),
-                    new SpanInfo("<LINK1>", "</LINK1>", clickableTermsSpan),
-                    new SpanInfo("<LINK2>", "</LINK2>", clickablePrivacySpan));
+            tosText = SpanApplier.applySpans(getString(R.string.fre_tos),
+                    new SpanInfo("<LINK1>", "</LINK1>", clickableGoogleTermsSpan),
+                    new SpanInfo("<LINK2>", "</LINK2>", clickableChromeAdditionalTermsSpan));
         }
-        mTosAndPrivacy.setText(tosAndPrivacyText);
+        mTosAndPrivacy.setText(tosText);
 
         // If this page should be skipped, it can be one of the following cases:
         //   1. Native hasn't been initialized yet and this page will be skipped once that happens.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
index 486eaf8..84574c0 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/AddLanguageFragment.java
@@ -24,7 +24,7 @@
 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 import java.util.ArrayList;
 import java.util.List;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
index 0d5917f..ef75775 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/language/settings/LanguageSettings.java
@@ -15,9 +15,9 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Settings fragment that displays information about Chrome languages, which allow users to
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragment.java
index 17dc8a4..02a3098 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/night_mode/settings/ThemeSettingsFragment.java
@@ -17,7 +17,7 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.night_mode.NightModeUtils;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.ui.UiUtils;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/notifications/settings/NotificationSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/notifications/settings/NotificationSettings.java
index d1bfeeb..bdb3f61 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/notifications/settings/NotificationSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/notifications/settings/NotificationSettings.java
@@ -14,12 +14,12 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchConfiguration;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchPrefs;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.site_settings.ContentSettingsResources;
 import org.chromium.chrome.browser.site_settings.SingleCategorySettings;
 import org.chromium.chrome.browser.site_settings.SiteSettingsCategory;
 import org.chromium.chrome.browser.site_settings.WebsitePreferenceBridge;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.content_settings.ContentSettingsType;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
index c7c72f8f..ea85b2a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/RecentTabsPage.java
@@ -310,9 +310,18 @@
     @Override
     public void onBottomControlsHeightChanged(
             int bottomControlsHeight, int bottomControlsMinHeight) {
+        updatePadding();
+    }
+
+    @Override
+    public void onTopControlsHeightChanged(int topControlsHeight, int topControlsMinHeight) {
+        updatePadding();
+    }
+
+    private void updatePadding() {
         final View recentTabsRoot = mView.findViewById(R.id.recent_tabs_root);
         ViewCompat.setPaddingRelative(recentTabsRoot, ViewCompat.getPaddingStart(recentTabsRoot),
                 mFullscreenManager.getTopControlsHeight(), ViewCompat.getPaddingEnd(recentTabsRoot),
-                bottomControlsHeight);
+                mFullscreenManager.getBottomControlsHeight());
     }
 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryViewer.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryViewer.java
index a02c8a9..064d28a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryViewer.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordEntryViewer.java
@@ -37,8 +37,8 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.ui.text.SpanApplier;
 import org.chromium.ui.widget.Toast;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
index 39247913..d80a25e4 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettings.java
@@ -33,14 +33,14 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
-import org.chromium.chrome.browser.settings.ChromeBasePreference;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SearchUtils;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.TextMessagePreference;
 import org.chromium.chrome.browser.webauth.authenticator.CableAuthenticatorUIFactory;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SearchUtils;
+import org.chromium.components.browser_ui.settings.TextMessagePreference;
 import org.chromium.ui.text.SpanApplier;
 
 import java.util.Locale;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java
index 05623db7..ce1e70ab 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/DoNotTrackSettings.java
@@ -11,8 +11,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 /**
  * Fragment to manage 'Do Not Track' preference and to explain to the user what it does.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
index a290c8c0..6de2a5a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/privacy/settings/PrivacySettings.java
@@ -20,13 +20,13 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ManagedPreferenceDelegate;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.sync.settings.SyncAndServicesSettings;
 import org.chromium.chrome.browser.usage_stats.UsageStatsConsentDialog;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.ui.text.NoUnderlineClickableSpan;
 import org.chromium.ui.text.SpanApplier;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
index 968a97fe..41d1d927 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/MainSettings.java
@@ -32,6 +32,9 @@
 import org.chromium.chrome.browser.sync.settings.SignInPreference;
 import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils;
 import org.chromium.chrome.browser.tracing.settings.DeveloperSettings;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
index fcbc7e1b..8ea27dd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/SettingsActivity.java
@@ -31,6 +31,9 @@
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileManagerUtils;
+import org.chromium.chrome.browser.site_settings.ChromeSiteSettingsClient;
+import org.chromium.chrome.browser.site_settings.SiteSettingsPreferenceFragment;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.ui.UiUtils;
 import org.chromium.ui.util.ColorUtils;
 
@@ -259,6 +262,14 @@
         }
     }
 
+    @Override
+    public void onAttachFragment(Fragment fragment) {
+        if (fragment instanceof SiteSettingsPreferenceFragment) {
+            ((SiteSettingsPreferenceFragment) fragment)
+                    .setSiteSettingsClient(new ChromeSiteSettingsClient());
+        }
+    }
+
     private void ensureActivityNotExported() {
         if (sActivityNotExportedChecked) return;
         sActivityNotExportedChecked = true;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageEditor.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageEditor.java
index b8def92c..f1b62db 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageEditor.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageEditor.java
@@ -17,7 +17,7 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.url_formatter.UrlFormatter;
 import org.chromium.url.GURL;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java
index 720e4749..fadfd31 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/settings/homepage/HomepageSettings.java
@@ -17,12 +17,12 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
-import org.chromium.chrome.browser.settings.TextMessagePreference;
 import org.chromium.chrome.browser.settings.homepage.RadioButtonGroupHomepagePreference.HomepageOption;
 import org.chromium.chrome.browser.settings.homepage.RadioButtonGroupHomepagePreference.PreferenceValues;
 import org.chromium.chrome.browser.toolbar.bottom.BottomToolbarVariationManager;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.TextMessagePreference;
 import org.chromium.components.url_formatter.UrlFormatter;
 
 /**
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
index a3d96baa..766baee 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/ConfirmImportSyncDataDialog.java
@@ -16,7 +16,7 @@
 
 import org.chromium.base.metrics.RecordUserAction;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 
 import java.util.Arrays;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
index 30b0e17..8a5fe86e 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/signin/SigninUtils.java
@@ -17,8 +17,8 @@
 import org.chromium.base.ThreadUtils;
 import org.chromium.base.annotations.CalledByNative;
 import org.chromium.base.annotations.NativeMethods;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
 import org.chromium.chrome.browser.sync.settings.AccountManagementFragment;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 import org.chromium.components.signin.GAIAServiceType;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
 import org.chromium.ui.base.WindowAndroid;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChosenObjectSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChosenObjectSettings.java
index ef3dbe8..e3ad44ad 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChosenObjectSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChosenObjectSettings.java
@@ -17,7 +17,6 @@
 import androidx.appcompat.widget.SearchView;
 import androidx.core.view.MenuItemCompat;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceScreen;
 import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;
 
@@ -25,9 +24,8 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.ChromeImageViewPreference;
-import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
+import org.chromium.components.browser_ui.settings.ChromeImageViewPreference;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -37,7 +35,7 @@
  * Shows a particular chosen object (e.g. a USB device) and the list of sites that have been
  * granted access to it by the user.
  */
-public class ChosenObjectSettings extends PreferenceFragmentCompat {
+public class ChosenObjectSettings extends SiteSettingsPreferenceFragment {
     public static final String EXTRA_OBJECT_INFOS = "org.chromium.chrome.preferences.object_infos";
     public static final String EXTRA_SITES = "org.chromium.chrome.preferences.site_set";
     public static final String EXTRA_CATEGORY =
@@ -287,18 +285,14 @@
                         getInfo();
                     });
 
-            preference.setManagedPreferenceDelegate(new ChromeManagedPreferenceDelegate() {
+            preference.setManagedPreferenceDelegate(new ForwardingManagedPreferenceDelegate(
+                    getSiteSettingsClient().getManagedPreferenceDelegate()) {
                 @Override
                 public boolean isPreferenceControlledByPolicy(Preference preference) {
                     return info.isManaged();
                 }
 
                 @Override
-                public boolean isPreferenceControlledByCustodian(Preference preference) {
-                    return false;
-                }
-
-                @Override
                 public boolean isPreferenceClickDisabledByPolicy(Preference preference) {
                     return false;
                 }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsClient.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsClient.java
new file mode 100644
index 0000000..2e4922f1
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ChromeSiteSettingsClient.java
@@ -0,0 +1,30 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.site_settings;
+
+import androidx.preference.Preference;
+
+import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
+
+/**
+ * A SiteSettingsClient instance that contains Chrome-specific Site Settings logic.
+ */
+public class ChromeSiteSettingsClient implements SiteSettingsClient {
+    private ManagedPreferenceDelegate mManagedPreferenceDelegate;
+
+    @Override
+    public ManagedPreferenceDelegate getManagedPreferenceDelegate() {
+        if (mManagedPreferenceDelegate == null) {
+            mManagedPreferenceDelegate = new ChromeManagedPreferenceDelegate() {
+                @Override
+                public boolean isPreferenceControlledByPolicy(Preference preference) {
+                    return false;
+                }
+            };
+        }
+        return mManagedPreferenceDelegate;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ForwardingManagedPreferenceDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ForwardingManagedPreferenceDelegate.java
new file mode 100644
index 0000000..f9d0511
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/ForwardingManagedPreferenceDelegate.java
@@ -0,0 +1,45 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.site_settings;
+
+import androidx.preference.Preference;
+
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
+
+/**
+ * A ManagedPreferenceDelegate that forwards all method calls to a base ManagedPreferenceDelegate
+ * instance.
+ *
+ * Methods in this class should be overridden to provide custom behavior. Non-overridden methods
+ * will forward to the base implementation, which will typically be the embedder-provided
+ * ManagedPreferenceDelegate instance.
+ */
+class ForwardingManagedPreferenceDelegate implements ManagedPreferenceDelegate {
+    private final ManagedPreferenceDelegate mBase;
+
+    public ForwardingManagedPreferenceDelegate(ManagedPreferenceDelegate base) {
+        this.mBase = base;
+    }
+
+    @Override
+    public boolean isPreferenceControlledByPolicy(Preference preference) {
+        return mBase.isPreferenceControlledByPolicy(preference);
+    }
+
+    @Override
+    public boolean isPreferenceControlledByCustodian(Preference preference) {
+        return mBase.isPreferenceControlledByCustodian(preference);
+    }
+
+    @Override
+    public boolean doesProfileHaveMultipleCustodians() {
+        return mBase.doesProfileHaveMultipleCustodians();
+    }
+
+    @Override
+    public boolean isPreferenceClickDisabledByPolicy(Preference preference) {
+        return mBase.isPreferenceClickDisabledByPolicy(preference);
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java
index 2d63223..c5bfbdd 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/FourStateCookieSettingsPreference.java
@@ -16,7 +16,7 @@
 
 import org.chromium.base.ApiCompatibilityUtils;
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 import org.chromium.components.browser_ui.widget.text.TextViewWithCompoundDrawables;
 import org.chromium.components.content_settings.CookieControlsMode;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/README.md b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/README.md
index cacf50b..bc8e281 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/README.md
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/README.md
@@ -26,7 +26,7 @@
     [SingleWebsiteSettings](https://cs.chromium.org/chromium/src/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettings.java?type=cs&g=0)
     class.
 
-All of these implement `PreferenceFragmentCompat` and use a layout xml to define
+All of these implement `SiteSettingsPreferenceFragment` and use a layout xml to define
 the preferences that form the screens.
 
 ## Site Settings
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
index a9068e38..55dbf87 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleCategorySettings.java
@@ -4,8 +4,8 @@
 
 package org.chromium.chrome.browser.site_settings;
 
-import static org.chromium.chrome.browser.settings.SearchUtils.handleSearchNavigation;
 import static org.chromium.chrome.browser.site_settings.WebsitePreferenceBridge.SITE_WILDCARD;
+import static org.chromium.components.browser_ui.settings.SearchUtils.handleSearchNavigation;
 
 import android.content.Context;
 import android.content.DialogInterface;
@@ -28,7 +28,6 @@
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceGroup;
 import androidx.preference.PreferenceManager;
 import androidx.preference.PreferenceScreen;
@@ -44,16 +43,16 @@
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
-import org.chromium.chrome.browser.settings.ChromeBasePreference;
-import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.ExpandablePreferenceGroup;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
-import org.chromium.chrome.browser.settings.SearchUtils;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.site_settings.FourStateCookieSettingsPreference.CookieSettingsState;
 import org.chromium.chrome.browser.site_settings.Website.StoredDataClearedCallback;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.ExpandablePreferenceGroup;
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
+import org.chromium.components.browser_ui.settings.SearchUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.content_settings.CookieControlsMode;
 import org.chromium.components.embedder_support.util.UrlUtilities;
@@ -75,7 +74,7 @@
  * the websites with microphone permissions. When the user selects a site, SingleWebsiteSettings
  * is launched to allow the user to see or modify the settings for that particular website.
  */
-public class SingleCategorySettings extends PreferenceFragmentCompat
+public class SingleCategorySettings extends SiteSettingsPreferenceFragment
         implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
                    AddExceptionPreference.SiteAddedCallback, View.OnClickListener,
                    PreferenceManager.OnPreferenceTreeClickListener {
@@ -168,7 +167,11 @@
 
     /** Called by common settings code to determine if a Preference is managed. */
     private class SingleCategoryManagedPreferenceDelegate
-            implements ChromeManagedPreferenceDelegate {
+            extends ForwardingManagedPreferenceDelegate {
+        SingleCategoryManagedPreferenceDelegate(ManagedPreferenceDelegate base) {
+            super(base);
+        }
+
         @Override
         public boolean isPreferenceControlledByPolicy(Preference preference) {
             // TODO(bauerb): Align the ManagedPreferenceDelegate and
@@ -1053,7 +1056,8 @@
         }
         binaryToggle.setSummaryOff(ContentSettingsResources.getDisabledSummary(contentType));
 
-        binaryToggle.setManagedPreferenceDelegate(new SingleCategoryManagedPreferenceDelegate());
+        binaryToggle.setManagedPreferenceDelegate(new SingleCategoryManagedPreferenceDelegate(
+                getSiteSettingsClient().getManagedPreferenceDelegate()));
 
         // Set the checked value.
         if (mCategory.showSites(SiteSettingsCategory.Type.DEVICE_LOCATION)) {
@@ -1071,10 +1075,14 @@
                 PrefServiceBridge.getInstance().getBoolean(Pref.BLOCK_THIRD_PARTY_COOKIES));
         thirdPartyCookiesPref.setEnabled(
                 WebsitePreferenceBridge.isCategoryEnabled(ContentSettingsType.COOKIES));
-        thirdPartyCookiesPref.setManagedPreferenceDelegate(
-                (ChromeManagedPreferenceDelegate) preference
-                -> PrefServiceBridge.getInstance().isManagedPreference(
-                        Pref.BLOCK_THIRD_PARTY_COOKIES));
+        thirdPartyCookiesPref.setManagedPreferenceDelegate(new ForwardingManagedPreferenceDelegate(
+                getSiteSettingsClient().getManagedPreferenceDelegate()) {
+            @Override
+            public boolean isPreferenceControlledByPolicy(Preference preference) {
+                return PrefServiceBridge.getInstance().isManagedPreference(
+                        Pref.BLOCK_THIRD_PARTY_COOKIES);
+            }
+        });
     }
 
     private void updateNotificationsSecondaryControls() {
@@ -1110,8 +1118,9 @@
 
     private void showManagedToast() {
         if (mCategory.isManagedByCustodian()) {
-            ManagedPreferencesUtils.showManagedByParentToast(
-                    getActivity(), new SingleCategoryManagedPreferenceDelegate());
+            ManagedPreferencesUtils.showManagedByParentToast(getActivity(),
+                    new SingleCategoryManagedPreferenceDelegate(
+                            getSiteSettingsClient().getManagedPreferenceDelegate()));
         } else {
             ManagedPreferencesUtils.showManagedByAdministratorToast(getActivity());
         }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettings.java
index 7e733a2..a093edc 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SingleWebsiteSettings.java
@@ -22,7 +22,6 @@
 import androidx.appcompat.app.AlertDialog;
 import androidx.preference.ListPreference;
 import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceScreen;
 
 import org.chromium.base.Callback;
@@ -31,10 +30,9 @@
 import org.chromium.chrome.browser.browserservices.permissiondelegation.TrustedWebActivityPermissionManager;
 import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.ChromeImageViewPreference;
-import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.ChromeImageViewPreference;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.embedder_support.util.Origin;
 
@@ -46,7 +44,7 @@
 /**
  * Shows the permissions and other settings for a particular website.
  */
-public class SingleWebsiteSettings extends PreferenceFragmentCompat
+public class SingleWebsiteSettings extends SiteSettingsPreferenceFragment
         implements Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener {
     // SingleWebsiteSettings expects either EXTRA_SITE (a Website) or
     // EXTRA_SITE_ADDRESS (a WebsiteAddress) to be present (but not both). If
@@ -593,21 +591,12 @@
                         }
                     });
 
-            preference.setManagedPreferenceDelegate(new ChromeManagedPreferenceDelegate() {
+            preference.setManagedPreferenceDelegate(new ForwardingManagedPreferenceDelegate(
+                    getSiteSettingsClient().getManagedPreferenceDelegate()) {
                 @Override
                 public boolean isPreferenceControlledByPolicy(Preference preference) {
                     return info.isManaged();
                 }
-
-                @Override
-                public boolean isPreferenceControlledByCustodian(Preference preference) {
-                    return false;
-                }
-
-                @Override
-                public boolean isPreferenceClickDisabledByPolicy(Preference preference) {
-                    return info.isManaged();
-                }
             });
 
             if (info.isManaged()) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettings.java
index c5b7274..92717e7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettings.java
@@ -7,14 +7,13 @@
 import android.os.Bundle;
 
 import androidx.preference.Preference;
-import androidx.preference.PreferenceFragmentCompat;
 
 import org.chromium.base.CommandLine;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.site_settings.SiteSettingsCategory.Type;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.content_public.browser.ContentFeatureList;
 import org.chromium.content_public.common.ContentSwitches;
 
@@ -28,7 +27,7 @@
  * browser-wide.
  */
 public class SiteSettings
-        extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener {
+        extends SiteSettingsPreferenceFragment implements Preference.OnPreferenceClickListener {
     // The keys for each category shown on the Site Settings page
     // are defined in the SiteSettingsCategory.
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsClient.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsClient.java
new file mode 100644
index 0000000..f84f5669
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsClient.java
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.site_settings;
+
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
+
+/**
+ * An interface implemented by the embedder that allows the Site Settings UI to access
+ * embedder-specific logic.
+ */
+public interface SiteSettingsClient {
+    /**
+     * @return the ManagedPreferenceDelegate instance that should be used when rendering
+     *         Preferences.
+     */
+    ManagedPreferenceDelegate getManagedPreferenceDelegate();
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreference.java
index 6c33901..fd4cb7f1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreference.java
@@ -12,7 +12,7 @@
 import androidx.preference.PreferenceViewHolder;
 
 import org.chromium.chrome.R;
-import org.chromium.chrome.browser.settings.ChromeBasePreference;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
 
 /**
  * A custom preference for drawing Site Settings entries.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreferenceFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreferenceFragment.java
new file mode 100644
index 0000000..8f2a87d
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/SiteSettingsPreferenceFragment.java
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.chrome.browser.site_settings;
+
+import androidx.preference.PreferenceFragmentCompat;
+
+/**
+ * Preference fragment for showing the Site Settings UI.
+ */
+public abstract class SiteSettingsPreferenceFragment extends PreferenceFragmentCompat {
+    private SiteSettingsClient mSiteSettingsClient;
+
+    /**
+     * Sets the SiteSettingsClient instance this Fragment should use.
+     *
+     * This should be called by the embedding Activity.
+     */
+    public void setSiteSettingsClient(SiteSettingsClient client) {
+        assert mSiteSettingsClient == null;
+        mSiteSettingsClient = client;
+    }
+
+    /**
+     * @return the SiteSettingsClient instance to use when rendering the Site Settings UI.
+     */
+    protected SiteSettingsClient getSiteSettingsClient() {
+        assert mSiteSettingsClient != null : "SiteSettingsClient not set";
+        return mSiteSettingsClient;
+    }
+}
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/WebsitePreference.java b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/WebsitePreference.java
index 33f6e6ea..19f9040 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/site_settings/WebsitePreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/site_settings/WebsitePreference.java
@@ -20,10 +20,10 @@
 
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.ChromeImageViewPreference;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper;
 import org.chromium.chrome.browser.ui.favicon.FaviconHelper.FaviconImageCallback;
 import org.chromium.chrome.browser.ui.favicon.RoundedIconGenerator;
+import org.chromium.components.browser_ui.settings.ChromeImageViewPreference;
 
 /**
  * A preference that displays a website's favicon and URL and, optionally, the amount of local
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
index 27d06a9a..f6f73b7 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/AccountManagementFragment.java
@@ -36,7 +36,6 @@
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.profiles.ProfileAccountManagementMetrics;
-import org.chromium.chrome.browser.settings.ChromeBasePreference;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
@@ -48,6 +47,7 @@
 import org.chromium.chrome.browser.signin.SigninUtils;
 import org.chromium.chrome.browser.superviseduser.FilteringBehavior;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
 import org.chromium.components.signin.AccountManagerFacade;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.GAIAServiceType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
index ed809da..df17ffb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/ManageSyncSettings.java
@@ -31,14 +31,14 @@
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.help.HelpAndFeedback;
 import org.chromium.chrome.browser.profiles.Profile;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.sync.TrustedVaultClient;
 import org.chromium.chrome.browser.sync.ui.PassphraseCreationDialogFragment;
 import org.chromium.chrome.browser.sync.ui.PassphraseDialogFragment;
 import org.chromium.chrome.browser.sync.ui.PassphraseTypeDialogFragment;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.sync.ModelType;
 import org.chromium.components.sync.PassphraseType;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java
index 01433edc..7d4a8c1 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SignInPreference.java
@@ -18,7 +18,6 @@
 import org.chromium.chrome.browser.firstrun.FirstRunSignInProcessor;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
 import org.chromium.chrome.browser.signin.DisplayableProfileData;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.PersonalizedSigninPromoView;
@@ -29,6 +28,7 @@
 import org.chromium.chrome.browser.signin.SigninUtils;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.sync.ProfileSyncService.SyncStateChangedListener;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.AccountsChangeObserver;
 import org.chromium.components.signin.base.CoreAccountInfo;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
index 45a30435..8657d6a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/sync/settings/SyncAndServicesSettings.java
@@ -49,19 +49,19 @@
 import org.chromium.chrome.browser.privacy.settings.PrivacyPreferencesManager;
 import org.chromium.chrome.browser.profiles.Profile;
 import org.chromium.chrome.browser.safe_browsing.SafeBrowsingBridge;
-import org.chromium.chrome.browser.settings.ChromeBasePreference;
 import org.chromium.chrome.browser.settings.ChromeManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
-import org.chromium.chrome.browser.settings.ManagedPreferenceDelegate;
-import org.chromium.chrome.browser.settings.ManagedPreferencesUtils;
 import org.chromium.chrome.browser.settings.SettingsActivity;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.signin.IdentityServicesProvider;
 import org.chromium.chrome.browser.signin.UnifiedConsentServiceBridge;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
 import org.chromium.chrome.browser.sync.TrustedVaultClient;
 import org.chromium.chrome.browser.sync.settings.SyncSettingsUtils.SyncError;
 import org.chromium.chrome.browser.sync.ui.PassphraseDialogFragment;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
+import org.chromium.components.browser_ui.settings.ManagedPreferencesUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.signin.AccountManagerFacadeProvider;
 import org.chromium.components.signin.base.CoreAccountInfo;
 import org.chromium.components.signin.metrics.SigninAccessPoint;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java
index 6b41808..fa2221eb 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/DeveloperSettings.java
@@ -12,7 +12,7 @@
 import org.chromium.chrome.browser.ChromeVersionInfo;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.SettingsUtils;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 import org.chromium.components.version_info.Channel;
 import org.chromium.components.version_info.VersionConstants;
 
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java
index 7df5aa0..8b6b8076 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingCategoriesSettings.java
@@ -13,8 +13,8 @@
 import androidx.preference.PreferenceFragmentCompat;
 import androidx.preference.PreferenceScreen;
 
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
 import org.chromium.chrome.browser.tracing.TracingController;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
 
 import java.util.ArrayList;
 import java.util.Collections;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java
index a8b0f53..3c9712a 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/tracing/settings/TracingSettings.java
@@ -15,9 +15,9 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.SettingsUtils;
 import org.chromium.chrome.browser.tracing.TracingController;
 import org.chromium.chrome.browser.tracing.TracingNotificationManager;
+import org.chromium.components.browser_ui.settings.SettingsUtils;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
diff --git a/chrome/android/javatests/DEPS b/chrome/android/javatests/DEPS
index 63dee6fd..98a7baa30 100644
--- a/chrome/android/javatests/DEPS
+++ b/chrome/android/javatests/DEPS
@@ -11,6 +11,7 @@
   "+components/autofill/android/java/src/org/chromium/components/autofill",
   "+components/background_task_scheduler/android/java",
   "+components/bookmarks/common/android/java/src/org/chromium/components/bookmarks",
+  "+components/browser_ui/settings/android",
   "+components/browser_ui/styles/android",
   "+components/browser_ui/widget/android",
   "+components/crash/android",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
index 1216917b..9bfd11ff 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/accessibility/settings/AccessibilitySettingsTest.java
@@ -23,9 +23,9 @@
 import org.chromium.base.task.PostTask;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.accessibility.FontSizePrefs;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
 import org.chromium.chrome.browser.settings.SettingsActivityTest;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
 import org.chromium.content_public.browser.UiThreadTaskTraits;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPreferencesUiTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPreferencesUiTest.java
index a802fd5..18046663 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPreferencesUiTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/browserservices/permissiondelegation/TrustedWebActivityPreferencesUiTest.java
@@ -19,8 +19,6 @@
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.ChromeApplication;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.settings.ChromeImageViewPreference;
-import org.chromium.chrome.browser.settings.ExpandablePreferenceGroup;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.site_settings.SingleCategorySettings;
 import org.chromium.chrome.browser.site_settings.SingleWebsiteSettings;
@@ -30,6 +28,8 @@
 import org.chromium.chrome.browser.site_settings.WebsiteAddress;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.settings.ChromeImageViewPreference;
+import org.chromium.components.browser_ui.settings.ExpandablePreferenceGroup;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
index 91741d6..3f717c3 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/compositor/overlays/strip/TabStripTest.java
@@ -17,7 +17,6 @@
 import org.chromium.base.test.util.CallbackHelper;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
-import org.chromium.base.test.util.FlakyTest;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.browser.compositor.layouts.components.CompositorButton;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
@@ -101,7 +100,6 @@
     @LargeTest
     @Restriction(UiRestriction.RESTRICTION_TYPE_TABLET)
     @Feature({"TabStrip"})
-    @FlakyTest(message = "crbug.com/592961")
     public void testNewTabButtonWithManyTabs() throws Exception {
         ChromeTabUtils.newTabsFromMenu(
                 InstrumentationRegistry.getInstrumentation(), mActivityTestRule.getActivity(), 3);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
index 625f9ef..7541077 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/homepage/HomepagePolicyIntegrationTest.java
@@ -30,7 +30,6 @@
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.homepage.HomepageMetricsEnums.HomeButtonPreferenceState;
 import org.chromium.chrome.browser.settings.homepage.HomepageMetricsEnums.HomepageLocationType;
@@ -43,6 +42,7 @@
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.TabLoadObserver;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoCookieLeakageTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoCookieLeakageTest.java
index c74befa..4b48535 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoCookieLeakageTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/incognito/IncognitoCookieLeakageTest.java
@@ -119,7 +119,7 @@
     testCookiesDoNotLeakFromIncognitoToIncognito(
             String incognitoActivityType1, String incognitoActivityType2) throws TimeoutException {
         ActivityType incognitoActivity1 = ActivityType.valueOf(incognitoActivityType1);
-        ActivityType incognitoActivity2 = ActivityType.valueOf(incognitoActivityType1);
+        ActivityType incognitoActivity2 = ActivityType.valueOf(incognitoActivityType2);
 
         Tab setter_tab = incognitoActivity1.launchUrl(
                 mChromeActivityTestRule, mCustomTabActivityTestRule, mCookiesTestPage);
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/settings/NotificationSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/settings/NotificationSettingsTest.java
index 57af06d..f7c360da 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/settings/NotificationSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/notifications/settings/NotificationSettingsTest.java
@@ -23,7 +23,6 @@
 import org.chromium.base.test.util.DisableIf;
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.offlinepages.prefetch.PrefetchPrefs;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsActivityTest;
 import org.chromium.chrome.browser.site_settings.ContentSettingsResources;
@@ -33,6 +32,7 @@
 import org.chromium.chrome.browser.test.ScreenShooter;
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
index 2e2eadf..60a3884 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/LocationBarLayoutTest.java
@@ -29,6 +29,7 @@
 import org.chromium.base.metrics.RecordHistogram;
 import org.chromium.base.test.util.CommandLineFlags;
 import org.chromium.base.test.util.Feature;
+import org.chromium.base.test.util.Matchers;
 import org.chromium.base.test.util.Restriction;
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeActivity;
@@ -41,6 +42,9 @@
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.components.security_state.ConnectionSecurityLevel;
+import org.chromium.content_public.browser.test.util.ClickUtils;
+import org.chromium.content_public.browser.test.util.Criteria;
+import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 import org.chromium.ui.test.util.UiRestriction;
 
@@ -204,6 +208,17 @@
 
     @Test
     @SmallTest
+    public void testDeleteButton() throws ExecutionException {
+        setUrlBarTextAndFocus("testing");
+        Assert.assertEquals(getDeleteButton().getVisibility(), VISIBLE);
+        ClickUtils.clickButton(getDeleteButton());
+        CriteriaHelper.pollUiThread(
+                Criteria.checkThat(() -> getDeleteButton().getVisibility(), Matchers.not(VISIBLE)));
+        Assert.assertEquals("", getUrlText(getUrlBar()));
+    }
+
+    @Test
+    @SmallTest
     @EnableFeatures(ChromeFeatureList.QUERY_IN_OMNIBOX)
     @Feature({"QueryInOmnibox"})
     public void testIsViewShowingModelSearchTerms() {
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
index 788763f..1837458 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/omnibox/OmniboxTest.java
@@ -34,7 +34,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.omnibox.status.StatusViewCoordinator;
 import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteController.OnSuggestionsReceivedListener;
-import org.chromium.chrome.browser.omnibox.suggestions.AutocompleteCoordinatorTestUtils;
 import org.chromium.chrome.browser.omnibox.suggestions.OmniboxSuggestion;
 import org.chromium.chrome.browser.tab.EmptyTabObserver;
 import org.chromium.chrome.browser.tab.Tab;
@@ -44,20 +43,15 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ChromeTabUtils;
 import org.chromium.chrome.test.util.OmniboxTestUtils;
-import org.chromium.chrome.test.util.OmniboxTestUtils.SuggestionsResult;
-import org.chromium.chrome.test.util.OmniboxTestUtils.TestAutocompleteController;
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.KeyUtils;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
-import org.chromium.content_public.browser.test.util.TouchCommon;
-import org.chromium.content_public.browser.test.util.UiUtils;
 import org.chromium.net.test.EmbeddedTestServer;
 import org.chromium.net.test.ServerCertificate;
 
-import java.util.HashMap;
 import java.util.List;
 import java.util.concurrent.Callable;
 
@@ -159,127 +153,6 @@
                 }));
     }
 
-    /**
-     * Tests that focusing a url bar starts a zero suggest request.
-     */
-    @Test
-    @MediumTest
-    @Feature({"Omnibox"})
-    @RetryOnFailure
-    public void testRequestZeroSuggestOnFocus() {
-        final LocationBarLayout locationBar =
-                (LocationBarLayout) mActivityTestRule.getActivity().findViewById(R.id.location_bar);
-        final UrlBar urlBar = (UrlBar) mActivityTestRule.getActivity().findViewById(R.id.url_bar);
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> { urlBar.setText("http://www.example.com/"); });
-
-        final TestAutocompleteController controller = new TestAutocompleteController(locationBar,
-                sEmptySuggestionListener, new HashMap<String, List<SuggestionsResult>>());
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            AutocompleteCoordinatorTestUtils.setAutocompleteController(
-                    locationBar.getAutocompleteCoordinator(), controller);
-        });
-        Assert.assertEquals("Should not have any zero suggest requests yet", 0,
-                controller.numZeroSuggestRequests());
-
-        OmniboxTestUtils.toggleUrlBarFocus(urlBar, true);
-
-        CriteriaHelper.pollInstrumentationThread(Criteria.equals(1, new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return controller.numZeroSuggestRequests();
-            }
-        }));
-
-        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
-
-        Assert.assertFalse(controller.isStartAutocompleteCalled());
-    }
-
-    /**
-     * Tests that focusing a url bar starts a zero suggest request.
-     */
-    @Test
-    @DisableIf.
-    Build(sdk_is_greater_than = Build.VERSION_CODES.KITKAT, message = "crbug.com/1027549")
-    @MediumTest
-    @Feature({"Omnibox"})
-    @RetryOnFailure
-    public void testRequestZeroSuggestAfterDelete() throws InterruptedException {
-        final LocationBarLayout locationBar =
-                (LocationBarLayout) mActivityTestRule.getActivity().findViewById(R.id.location_bar);
-        final UrlBar urlBar = (UrlBar) mActivityTestRule.getActivity().findViewById(R.id.url_bar);
-        final ImageButton deleteButton =
-                (ImageButton) mActivityTestRule.getActivity().findViewById(R.id.delete_button);
-
-        final TestAutocompleteController controller = new TestAutocompleteController(locationBar,
-                sEmptySuggestionListener, new HashMap<String, List<SuggestionsResult>>());
-
-        OmniboxTestUtils.toggleUrlBarFocus(urlBar, true);
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            AutocompleteCoordinatorTestUtils.setAutocompleteController(
-                    locationBar.getAutocompleteCoordinator(), controller);
-            urlBar.setText("g");
-        });
-
-        CriteriaHelper.pollInstrumentationThread(
-                new Criteria("Should have drawn the delete button") {
-                    @Override
-                    public boolean isSatisfied() {
-                        return deleteButton.getWidth() > 0;
-                    }
-                });
-
-        // The click view below ends up clicking on the menu button underneath the delete button
-        // for some time after the delete button appears. Wait for UI to settle down before
-        // clicking.
-        UiUtils.settleDownUI(InstrumentationRegistry.getInstrumentation());
-
-        TouchCommon.singleClickView(deleteButton);
-
-        CriteriaHelper.pollInstrumentationThread(Criteria.equals(1, new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return controller.numZeroSuggestRequests();
-            }
-        }));
-    }
-
-    @Test
-    @DisableIf.
-    Build(sdk_is_greater_than = Build.VERSION_CODES.KITKAT, message = "crbug.com/1027549")
-    @MediumTest
-    @Feature({"Omnibox"})
-    @RetryOnFailure
-    public void testRequestZeroSuggestTypeAndBackspace() {
-        final LocationBarLayout locationBar =
-                (LocationBarLayout) mActivityTestRule.getActivity().findViewById(R.id.location_bar);
-        final UrlBar urlBar = (UrlBar) mActivityTestRule.getActivity().findViewById(R.id.url_bar);
-
-        final TestAutocompleteController controller = new TestAutocompleteController(locationBar,
-                sEmptySuggestionListener, new HashMap<String, List<SuggestionsResult>>());
-
-        OmniboxTestUtils.toggleUrlBarFocus(urlBar, true);
-
-        TestThreadUtils.runOnUiThreadBlocking(() -> {
-            AutocompleteCoordinatorTestUtils.setAutocompleteController(
-                    locationBar.getAutocompleteCoordinator(), controller);
-            urlBar.setText("g");
-            urlBar.setSelection(1);
-        });
-
-        Assert.assertEquals("No calls to zero suggest yet", 0, controller.numZeroSuggestRequests());
-        KeyUtils.singleKeyEventView(
-                InstrumentationRegistry.getInstrumentation(), urlBar, KeyEvent.KEYCODE_DEL);
-        CriteriaHelper.pollInstrumentationThread(Criteria.equals(1, new Callable<Integer>() {
-            @Override
-            public Integer call() {
-                return controller.numZeroSuggestRequests();
-            }
-        }));
-    }
-
     // Sanity check that no text is displayed in the omnibox when on the NTP page and that the hint
     // text is correct.
     @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
index a52504c..756494d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/partnercustomizations/PartnerHomepageIntegrationTest.java
@@ -27,7 +27,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.ChromeTabbedActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.homepage.HomepageEditor;
 import org.chromium.chrome.browser.settings.homepage.HomepageSettings;
@@ -37,6 +36,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.partnercustomizations.TestPartnerBrowserCustomizationsProvider;
 import org.chromium.chrome.test.util.ChromeTabUtils;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java
index e0695fa1..a7ca5d8 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordSettingsTest.java
@@ -96,8 +96,6 @@
 import org.chromium.chrome.browser.history.StubbedHistoryProvider;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsActivityTest;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
@@ -105,6 +103,8 @@
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.util.browser.Features;
 import org.chromium.chrome.test.util.browser.signin.SigninTestUtil;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.sync.ModelType;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordViewingTypeTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordViewingTypeTest.java
index adb9ac5..4a543f0 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordViewingTypeTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/password_manager/settings/PasswordViewingTypeTest.java
@@ -21,11 +21,11 @@
 
 import org.chromium.base.test.BaseJUnit4ClassRunner;
 import org.chromium.base.test.util.Feature;
-import org.chromium.chrome.browser.settings.ChromeBasePreference;
 import org.chromium.chrome.browser.settings.MainSettings;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.sync.ProfileSyncService;
+import org.chromium.components.browser_ui.settings.ChromeBasePreference;
 import org.chromium.components.signin.AccountUtils;
 import org.chromium.components.signin.test.util.AccountHolder;
 import org.chromium.components.signin.test.util.AccountManagerTestRule;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
index 342c854..576b23d 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/settings/SearchEngineSettingsTest.java
@@ -23,7 +23,6 @@
 import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
 import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
 import org.chromium.chrome.browser.settings.MainSettings;
-import org.chromium.chrome.browser.settings.ManagedPreferenceDelegate;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsActivityTest;
 import org.chromium.chrome.browser.site_settings.ContentSettingValues;
@@ -32,6 +31,7 @@
 import org.chromium.chrome.test.ChromeBrowserTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityUtils;
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
 import org.chromium.components.search_engines.TemplateUrl;
 import org.chromium.components.search_engines.TemplateUrlService;
 import org.chromium.components.search_engines.TemplateUrlService.LoadListener;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentTest.java
index 768f4fb..14fd323 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentTest.java
@@ -23,13 +23,13 @@
 import org.chromium.chrome.browser.ntp.NewTabPage;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
 import org.chromium.chrome.browser.partnercustomizations.PartnerBrowserCustomizations;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
-import org.chromium.chrome.browser.settings.TextMessagePreference;
 import org.chromium.chrome.browser.settings.homepage.HomepageMetricsEnums.HomepageLocationType;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
+import org.chromium.components.browser_ui.settings.TextMessagePreference;
 import org.chromium.components.browser_ui.widget.RadioButtonWithDescription;
 import org.chromium.components.browser_ui.widget.RadioButtonWithEditText;
 import org.chromium.components.embedder_support.util.UrlConstants;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentWithEditorTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentWithEditorTest.java
index a7acb9d..04ca352c 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentWithEditorTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/settings/homepage/HomepageSettingsFragmentWithEditorTest.java
@@ -23,10 +23,10 @@
 import org.chromium.chrome.browser.flags.ChromeFeatureList;
 import org.chromium.chrome.browser.homepage.HomepageTestRule;
 import org.chromium.chrome.browser.partnercustomizations.HomepageManager;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.util.browser.Features;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.embedder_support.util.UrlConstants;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
index 1301ded8..b342150 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/site_settings/SiteSettingsTest.java
@@ -35,8 +35,6 @@
 import org.chromium.chrome.browser.notifications.channels.SiteChannelsManager;
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
-import org.chromium.chrome.browser.settings.ChromeBaseCheckBoxPreference;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.site_settings.FourStateCookieSettingsPreference.CookieSettingsState;
@@ -46,6 +44,8 @@
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.LocationSettingsTestUtil;
+import org.chromium.components.browser_ui.settings.ChromeBaseCheckBoxPreference;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.content_settings.ContentSettingsType;
 import org.chromium.components.embedder_support.util.Origin;
 import org.chromium.components.permissions.nfc.NfcSystemLevelSetting;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
index c56eb90..2bab7a0a 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/ManageSyncSettingsTest.java
@@ -29,7 +29,6 @@
 import org.chromium.chrome.R;
 import org.chromium.chrome.browser.autofill.PersonalDataManager;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.sync.settings.ManageSyncSettings;
 import org.chromium.chrome.browser.sync.ui.PassphraseCreationDialogFragment;
@@ -38,6 +37,7 @@
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.ActivityUtils;
 import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.sync.ModelType;
 import org.chromium.components.sync.PassphraseType;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java
index fa5e70c8..4bf8f79 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/sync/SyncAndServicesSettingsTest.java
@@ -29,7 +29,6 @@
 import org.chromium.chrome.browser.flags.ChromeSwitches;
 import org.chromium.chrome.browser.preferences.ChromePreferenceKeys;
 import org.chromium.chrome.browser.preferences.SharedPreferencesManager;
-import org.chromium.chrome.browser.settings.ChromeSwitchPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
 import org.chromium.chrome.browser.sync.settings.SyncAndServicesSettings;
@@ -38,6 +37,7 @@
 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
 import org.chromium.chrome.test.util.browser.sync.SyncTestUtil;
+import org.chromium.components.browser_ui.settings.ChromeSwitchPreference;
 import org.chromium.components.sync.AndroidSyncSettings;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
 
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java
index 2874d36..5d5f8ac 100644
--- a/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java
+++ b/chrome/android/javatests/src/org/chromium/chrome/browser/tracing/settings/TracingSettingsTest.java
@@ -34,15 +34,15 @@
 import org.chromium.base.test.util.Feature;
 import org.chromium.chrome.browser.ChromeActivity;
 import org.chromium.chrome.browser.flags.ChromeSwitches;
-import org.chromium.chrome.browser.settings.ButtonPreference;
 import org.chromium.chrome.browser.settings.SettingsActivity;
 import org.chromium.chrome.browser.settings.SettingsLauncher;
-import org.chromium.chrome.browser.settings.TextMessagePreference;
 import org.chromium.chrome.browser.tracing.TracingController;
 import org.chromium.chrome.browser.tracing.TracingNotificationManager;
 import org.chromium.chrome.test.ChromeActivityTestRule;
 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
 import org.chromium.chrome.test.util.browser.notifications.MockNotificationManagerProxy;
+import org.chromium.components.browser_ui.settings.ButtonPreference;
+import org.chromium.components.browser_ui.settings.TextMessagePreference;
 import org.chromium.content_public.browser.test.util.Criteria;
 import org.chromium.content_public.browser.test.util.CriteriaHelper;
 import org.chromium.content_public.browser.test.util.TestThreadUtils;
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
index 1d68855..d7a265f 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/omnibox/suggestions/AutocompleteMediatorUnitTest.java
@@ -476,4 +476,58 @@
                             SuggestionCommonProperties.LAYOUT_DIRECTION));
         }
     }
+
+    @Test
+    @Features.DisableFeatures({ChromeFeatureList.OMNIBOX_ADAPTIVE_SUGGESTIONS_COUNT,
+            ChromeFeatureList.OMNIBOX_DEFERRED_KEYBOARD_POPUP})
+    public void onUrlFocusChange_triggersZeroSuggest_nativeInitialized() {
+        when(mAutocompleteDelegate.isUrlBarFocused()).thenReturn(true);
+        when(mAutocompleteDelegate.didFocusUrlFromFakebox()).thenReturn(false);
+
+        Profile profile = Mockito.mock(Profile.class);
+        String url = "http://www.example.com";
+        String title = "Title";
+        int pageClassification = 2;
+        when(mToolbarDataProvider.getProfile()).thenReturn(profile);
+        when(mToolbarDataProvider.getCurrentUrl()).thenReturn(url);
+        when(mToolbarDataProvider.getTitle()).thenReturn(title);
+        when(mToolbarDataProvider.hasTab()).thenReturn(true);
+        when(mToolbarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+
+        when(mTextStateProvider.getTextWithAutocomplete()).thenReturn(url);
+
+        mMediator.onNativeInitialized();
+        mMediator.onUrlFocusChange(true);
+        verify(mAutocompleteController)
+                .startZeroSuggest(profile, url, url, pageClassification, title);
+    }
+
+    @Test
+    @Features.DisableFeatures({ChromeFeatureList.OMNIBOX_ADAPTIVE_SUGGESTIONS_COUNT,
+            ChromeFeatureList.OMNIBOX_DEFERRED_KEYBOARD_POPUP})
+    public void onUrlFocusChange_triggersZeroSuggest_nativeNotInitialized() {
+        when(mAutocompleteDelegate.isUrlBarFocused()).thenReturn(true);
+        when(mAutocompleteDelegate.didFocusUrlFromFakebox()).thenReturn(false);
+
+        Profile profile = Mockito.mock(Profile.class);
+        String url = "http://www.example.com";
+        String title = "Title";
+        int pageClassification = 2;
+        when(mToolbarDataProvider.getProfile()).thenReturn(profile);
+        when(mToolbarDataProvider.getCurrentUrl()).thenReturn(url);
+        when(mToolbarDataProvider.getTitle()).thenReturn(title);
+        when(mToolbarDataProvider.hasTab()).thenReturn(true);
+        when(mToolbarDataProvider.getPageClassification(false)).thenReturn(pageClassification);
+
+        when(mTextStateProvider.getTextWithAutocomplete()).thenReturn("");
+
+        // Signal focus prior to initializing native.
+        mMediator.onUrlFocusChange(true);
+
+        // Initialize native and ensure zero suggest is triggered.
+        mMediator.onNativeInitialized();
+        ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
+        verify(mAutocompleteController)
+                .startZeroSuggest(profile, "", url, pageClassification, title);
+    }
 }
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index c68dc93..c356b26 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -3088,8 +3088,6 @@
     }
   } else {  # !is_android
     sources += [
-      "accessibility/caption_bubble.cc",
-      "accessibility/caption_bubble.h",
       "accessibility/invert_bubble_prefs.cc",
       "accessibility/invert_bubble_prefs.h",
       "apps/app_service/app_icon_factory.cc",
diff --git a/chrome/browser/DEPS b/chrome/browser/DEPS
index 1940ec8..99d8bab 100644
--- a/chrome/browser/DEPS
+++ b/chrome/browser/DEPS
@@ -40,6 +40,7 @@
   "+components/bookmarks/common",
   "+components/bookmarks/managed",
   "+components/bookmarks/test",
+  "+components/browser_ui/settings",
   "+components/browser_sync",
   "+components/browser_watcher",
   "+components/browsing_data/content",
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 46b9132..003fa2b 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -235,13 +235,6 @@
   void Pin();
   void Unpin();
 
-  // |metrics_services_manager_| owns this.
-  ChromeMetricsServicesManagerClient* metrics_services_manager_client_ =
-      nullptr;
-
-  std::unique_ptr<metrics_services_manager::MetricsServicesManager>
-      metrics_services_manager_;
-
   bool created_watchdog_thread_ = false;
   std::unique_ptr<WatchDogThread> watchdog_thread_;
 
@@ -255,6 +248,14 @@
 
   std::unique_ptr<PrefService> local_state_;
 
+  // |metrics_services_manager_| owns this.
+  ChromeMetricsServicesManagerClient* metrics_services_manager_client_ =
+      nullptr;
+
+  // Must be destroyed before |local_state_|.
+  std::unique_ptr<metrics_services_manager::MetricsServicesManager>
+      metrics_services_manager_;
+
   std::unique_ptr<network::NetworkQualityTracker> network_quality_tracker_;
 
   // Listens to NetworkQualityTracker and sends network quality updates to the
diff --git a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
index 201bab8..82e4ca4 100644
--- a/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
+++ b/chrome/browser/chromeos/accessibility/spoken_feedback_browsertest.cc
@@ -390,7 +390,8 @@
 
 // Verifies that speaking text under mouse works for Shelf button and voice
 // announcements should not be stacked when mouse goes over many Shelf buttons
-IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest, SpeakingTextUnderMouseForShelfItem) {
+IN_PROC_BROWSER_TEST_P(SpokenFeedbackTest,
+                       DISABLED_SpeakingTextUnderMouseForShelfItem) {
   // Add the ShelfItem to the ShelfModel after enabling the ChromeVox. Because
   // when an extension is enabled, the ShelfItems which are not recorded as
   // pinned apps in user preference will be removed.
diff --git a/chrome/browser/chromeos/arc/session/OWNERS b/chrome/browser/chromeos/arc/session/OWNERS
new file mode 100644
index 0000000..8edfc24
--- /dev/null
+++ b/chrome/browser/chromeos/arc/session/OWNERS
@@ -0,0 +1 @@
+file://components/arc/session/OWNERS
diff --git a/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry_unittest.cc b/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry_unittest.cc
index 66b488a..ece28be9 100644
--- a/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry_unittest.cc
+++ b/chrome/browser/chromeos/child_accounts/time_limits/app_activity_registry_unittest.cc
@@ -69,9 +69,7 @@
 
 class AppActivityRegistryTest : public ChromeViewsTestBase {
  protected:
-  AppActivityRegistryTest()
-      : ChromeViewsTestBase(
-            base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
+  AppActivityRegistryTest() = default;
   AppActivityRegistryTest(const AppActivityRegistryTest&) = delete;
   AppActivityRegistryTest& operator=(const AppActivityRegistryTest&) = delete;
   ~AppActivityRegistryTest() override = default;
@@ -98,7 +96,6 @@
     return *registry_test_;
   }
 
-  base::test::TaskEnvironment& task_environment() { return task_environment_; }
   AppTimeNotificationDelegateMock& notification_delegate_mock() {
     return notification_delegate_mock_;
   }
@@ -152,7 +149,7 @@
     const AppId& app_id,
     const base::Optional<AppLimit>& app_limit) {
   registry().SetAppLimit(app_id, app_limit);
-  task_environment_.RunUntilIdle();
+  task_environment()->RunUntilIdle();
 }
 
 void AppActivityRegistryTest::ReInitializeRegistry() {
@@ -168,7 +165,7 @@
     base::TimeDelta activity_length) {
   auto* app_window = CreateWindowForApp(app_id);
   registry().OnAppActive(app_id, app_window, base::Time::Now());
-  task_environment().FastForwardBy(activity_length);
+  task_environment()->FastForwardBy(activity_length);
   registry().OnAppInactive(app_id, app_window, base::Time::Now());
 }
 
@@ -178,11 +175,11 @@
   base::Time app1_start_time = base::Time::Now();
   base::TimeDelta active_time = base::TimeDelta::FromMinutes(5);
   registry().OnAppActive(kApp1, app1_window, app1_start_time);
-  task_environment().FastForwardBy(active_time / 2);
+  task_environment()->FastForwardBy(active_time / 2);
   EXPECT_EQ(active_time / 2, registry().GetActiveTime(kApp1));
   EXPECT_TRUE(registry().IsAppActive(kApp1));
 
-  task_environment().FastForwardBy(active_time / 2);
+  task_environment()->FastForwardBy(active_time / 2);
   base::Time app1_end_time = base::Time::Now();
   registry().OnAppInactive(kApp1, app1_window, app1_end_time);
   EXPECT_EQ(active_time, registry().GetActiveTime(kApp1));
@@ -196,14 +193,14 @@
   base::TimeDelta app2_active_time = base::TimeDelta::FromMinutes(5);
 
   registry().OnAppActive(kApp2, app2_window1, base::Time::Now());
-  task_environment().FastForwardBy(app2_active_time / 2);
+  task_environment()->FastForwardBy(app2_active_time / 2);
 
   registry().OnAppActive(kApp2, app2_window2, base::Time::Now());
   registry().OnAppInactive(kApp2, app2_window1, base::Time::Now());
   registry().OnAppInactive(kApp2, app2_window1, base::Time::Now());
   EXPECT_TRUE(registry().IsAppActive(kApp2));
 
-  task_environment().FastForwardBy(app2_active_time / 2);
+  task_environment()->FastForwardBy(app2_active_time / 2);
 
   // Repeated calls to OnAppInactive shouldn't affect the time calculation.
   registry().OnAppInactive(kApp2, app2_window1, base::Time::Now());
@@ -218,14 +215,14 @@
   base::TimeDelta app2_inactive_time = base::TimeDelta::FromMinutes(1);
 
   registry().OnAppActive(kApp2, app2_window1, base::Time::Now());
-  task_environment().FastForwardBy(app2_active_time / 2);
+  task_environment()->FastForwardBy(app2_active_time / 2);
 
   registry().OnAppInactive(kApp2, app2_window1, base::Time::Now());
-  task_environment().FastForwardBy(app2_inactive_time);
+  task_environment()->FastForwardBy(app2_inactive_time);
   EXPECT_FALSE(registry().IsAppActive(kApp2));
 
   registry().OnAppActive(kApp2, app2_window2, base::Time::Now());
-  task_environment().FastForwardBy(app2_active_time / 2);
+  task_environment()->FastForwardBy(app2_active_time / 2);
 
   registry().OnAppInactive(kApp2, app2_window1, base::Time::Now());
   EXPECT_TRUE(registry().IsAppActive(kApp2));
@@ -256,7 +253,7 @@
       ShowAppTimeLimitNotification(
           kApp1, testing::_, chromeos::app_time::AppNotification::kFiveMinutes))
       .Times(1);
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(5));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(5));
   EXPECT_EQ(base::TimeDelta::FromMinutes(5), registry().GetActiveTime(kApp1));
   EXPECT_TRUE(registry().IsAppActive(kApp1));
 
@@ -266,7 +263,7 @@
       ShowAppTimeLimitNotification(
           kApp1, testing::_, chromeos::app_time::AppNotification::kOneMinute))
       .Times(1);
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(4));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(4));
   EXPECT_EQ(base::TimeDelta::FromMinutes(9), registry().GetActiveTime(kApp1));
 
   // Expect time limit reached notification.
@@ -275,7 +272,7 @@
                   kApp1, testing::_,
                   chromeos::app_time::AppNotification::kTimeLimitReached))
       .Times(1);
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(1));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(1));
   EXPECT_EQ(base::TimeDelta::FromMinutes(10), registry().GetActiveTime(kApp1));
 
   EXPECT_EQ(registry().GetAppState(kApp1), AppState::kLimitReached);
@@ -294,7 +291,7 @@
   base::TimeDelta active_time = base::TimeDelta::FromMinutes(10);
   registry().OnAppActive(kApp1, app1_window, start);
 
-  task_environment().FastForwardBy(active_time);
+  task_environment()->FastForwardBy(active_time);
 
   const AppLimit new_limit(AppRestriction::kTimeLimit,
                            base::TimeDelta::FromMinutes(14),
@@ -307,7 +304,7 @@
       ShowAppTimeLimitNotification(
           kApp1, testing::_, chromeos::app_time::AppNotification::kOneMinute))
       .Times(1);
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(3));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(3));
 }
 
 TEST_F(AppActivityRegistryTest, SkippedAllNotifications) {
@@ -323,7 +320,7 @@
   base::TimeDelta active_time = base::TimeDelta::FromMinutes(10);
   registry().OnAppActive(kApp1, app1_window, start);
 
-  task_environment().FastForwardBy(active_time);
+  task_environment()->FastForwardBy(active_time);
 
   // Notice that the 5 minute and 1 minute notifications are jumped.
   const AppLimit new_limit(AppRestriction::kTimeLimit,
@@ -347,7 +344,7 @@
   // There are going to be a bunch of mock notification calls for kFiveMinutes,
   // kOneMinute, and kTimeLimitReached. They have already been tested in the
   // other tests. Let's igonre them.
-  task_environment().FastForwardBy(kTenMinutes);
+  task_environment()->FastForwardBy(kTenMinutes);
 
   EXPECT_EQ(registry().GetAppState(kApp1), AppState::kLimitReached);
 
@@ -374,7 +371,7 @@
   registry().OnAppActive(kApp1, app1_window, start);
   registry().OnAppActive(kApp2, app2_window, start);
 
-  task_environment().FastForwardBy(kTenMinutes);
+  task_environment()->FastForwardBy(kTenMinutes);
 
   // App 1's time limit has been reached.
   EXPECT_TRUE(registry().IsAppTimeLimitReached(kApp1));
@@ -394,7 +391,7 @@
   // Now make sure that the timers have been scheduled appropriately.
   registry().OnAppActive(kApp1, app1_window, start);
 
-  task_environment().FastForwardBy(kTenMinutes);
+  task_environment()->FastForwardBy(kTenMinutes);
 
   EXPECT_TRUE(registry().IsAppTimeLimitReached(kApp1));
   EXPECT_EQ(kTenMinutes, registry().GetActiveTime(kApp1));
@@ -404,7 +401,7 @@
   EXPECT_EQ(kTenMinutes, registry().GetActiveTime(kApp2));
 
   // Now let's make sure
-  task_environment().FastForwardBy(kTenMinutes);
+  task_environment()->FastForwardBy(kTenMinutes);
   EXPECT_TRUE(registry().IsAppTimeLimitReached(kApp2));
   EXPECT_EQ(*limit2.daily_limit(), registry().GetActiveTime(kApp2));
 }
@@ -425,7 +422,7 @@
 
   // Make chrome active for 30 minutes.
   registry().OnChromeAppActivityChanged(ChromeAppActivityState::kActive, start);
-  task_environment().FastForwardBy(kHalfHour);
+  task_environment()->FastForwardBy(kHalfHour);
   registry().OnChromeAppActivityChanged(ChromeAppActivityState::kInactive,
                                         start + kHalfHour);
 
@@ -451,7 +448,7 @@
                   chromeos::app_time::AppNotification::kTimeLimitReached))
       .Times(1);
 
-  task_environment().FastForwardBy(kHalfHour);
+  task_environment()->FastForwardBy(kHalfHour);
 
   EXPECT_TRUE(registry().IsAppTimeLimitReached(kApp2));
   EXPECT_TRUE(registry().IsAppTimeLimitReached(kChromeAppId));
@@ -470,7 +467,7 @@
   EXPECT_EQ(base::nullopt, registry().GetTimeLimit(kApp1));
   EXPECT_EQ(base::nullopt, registry_test().GetTimeLeft(kApp1));
 
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(5));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(5));
 
   // Limit set for active app.
   const AppLimit limit1(AppRestriction::kTimeLimit,
@@ -483,7 +480,7 @@
   EXPECT_EQ(base::TimeDelta::FromMinutes(6),
             registry_test().GetTimeLeft(kApp1));
 
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(5));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(5));
 
   EXPECT_TRUE(registry().IsAppActive(kApp1));
   EXPECT_EQ(base::TimeDelta::FromMinutes(10), registry().GetActiveTime(kApp1));
@@ -524,7 +521,7 @@
   // Use available limit - app should become paused.
   auto* app1_window = CreateWindowForApp(kApp1);
   registry().OnAppActive(kApp1, app1_window, base::Time::Now());
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(5));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(5));
 
   EXPECT_FALSE(registry().IsAppActive(kApp1));
   EXPECT_TRUE(registry().IsAppTimeLimitReached(kApp1));
@@ -633,7 +630,7 @@
 
   base::Time app1_start_time_1 = base::Time::Now();
   registry().OnAppActive(kApp1, app1_window, app1_start_time_1);
-  task_environment().FastForwardBy(active_time / 2);
+  task_environment()->FastForwardBy(active_time / 2);
 
   // Save app activity.
   registry_test().SaveAppActivity();
@@ -642,11 +639,11 @@
   registry().OnAppInactive(kApp1, app1_window, app1_inactive_time_1);
 
   // App1 is inactive for 5 minutes.
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(5));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(5));
 
   base::Time app1_start_time_2 = base::Time::Now();
   registry().OnAppActive(kApp1, app1_window, app1_start_time_2);
-  task_environment().FastForwardBy(active_time / 2);
+  task_environment()->FastForwardBy(active_time / 2);
 
   // Time limit is reached. App becomes inactive.
   EXPECT_FALSE(registry().IsAppActive(kApp1));
@@ -701,7 +698,7 @@
 
   // App1 has been uninstalled.
   registry().OnAppUninstalled(kApp1);
-  task_environment().FastForwardBy(base::TimeDelta::FromMinutes(10));
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(10));
 
   // Removes kApp1 and cleans up ActiveTimes list in user pref.
   registry().OnSuccessfullyReported(base::Time::Now());
@@ -749,7 +746,7 @@
       prefs::kPerAppTimeLimitsLastSuccessfulReportTime,
       start_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
 
-  task_environment().FastForwardBy(base::TimeDelta::FromDays(30));
+  task_environment()->FastForwardBy(base::TimeDelta::FromDays(30));
 
   // Now let's recreate AppActivityRegistry. Its state should be restored.
   ReInitializeRegistry();
diff --git a/chrome/browser/chromeos/crostini/crostini_test_util.cc b/chrome/browser/chromeos/crostini/crostini_test_util.cc
index e148505..4704f6353 100644
--- a/chrome/browser/chromeos/crostini/crostini_test_util.cc
+++ b/chrome/browser/chromeos/crostini/crostini_test_util.cc
@@ -16,7 +16,7 @@
 // DialogDelegate::CreateDialogWidget().
 class TestViewsDelegateWithContext : public ChromeTestViewsDelegate<> {
  public:
-  TestViewsDelegateWithContext() {}
+  TestViewsDelegateWithContext() = default;
 
   void set_context(gfx::NativeWindow context) { context_ = context; }
 
@@ -40,8 +40,7 @@
 namespace crostini {
 
 void SetUpViewsEnvironmentForTesting() {
-  std::unique_ptr<TestViewsDelegateWithContext> views_delegate(
-      new TestViewsDelegateWithContext);
+  auto views_delegate = std::make_unique<TestViewsDelegateWithContext>();
   TestViewsDelegateWithContext* views_delegate_weak = views_delegate.get();
   views_helper_ =
       std::make_unique<views::ScopedViewsTestHelper>(std::move(views_delegate));
diff --git a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
index ddefba78..e615708 100644
--- a/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
+++ b/chrome/browser/chromeos/login/active_directory_login_browsertest.cc
@@ -4,7 +4,6 @@
 
 #include <string>
 
-#include "ash/public/cpp/ash_switches.h"
 #include "base/base_paths.h"
 #include "base/environment.h"
 #include "base/path_service.h"
@@ -232,23 +231,8 @@
   ad_login_.TestPasswordChangeOldPasswordError();
 }
 
-class ActiveDirectoryWebUILoginTest : public ActiveDirectoryLoginTest {
- public:
-  ActiveDirectoryWebUILoginTest() = default;
-  ~ActiveDirectoryWebUILoginTest() override = default;
-
-  // chromeos::ActiveDirectoryLoginTest:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    command_line->AppendSwitch(ash::switches::kShowWebUiLogin);
-    chromeos::ActiveDirectoryLoginTest::SetUpCommandLine(command_line);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ActiveDirectoryWebUILoginTest);
-};
-
 // Test reopening Active Directory password change screen clears errors.
-IN_PROC_BROWSER_TEST_F(ActiveDirectoryWebUILoginTest,
+IN_PROC_BROWSER_TEST_F(ActiveDirectoryLoginTest,
                        PasswordChange_ReopenClearErrors) {
   OobeBaseTest::WaitForSigninScreen();
   ASSERT_TRUE(InstallAttributes::Get()->IsActiveDirectoryManaged());
diff --git a/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc b/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc
index 2aa689d..0bbc42b 100644
--- a/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc
+++ b/chrome/browser/chromeos/login/demo_mode/demo_setup_browsertest.cc
@@ -19,6 +19,7 @@
 #include "build/build_config.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
 #include "chrome/browser/chromeos/login/demo_mode/demo_setup_test_utils.h"
+#include "chrome/browser/chromeos/login/login_manager_test.h"
 #include "chrome/browser/chromeos/login/mock_network_state_helper.h"
 #include "chrome/browser/chromeos/login/oobe_screen.h"
 #include "chrome/browser/chromeos/login/screens/demo_setup_screen.h"
@@ -26,7 +27,6 @@
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/test/enrollment_helper_mixin.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
-#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/test/test_condition_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
@@ -139,13 +139,22 @@
 }  // namespace
 
 // Basic tests for demo mode setup flow.
-class DemoSetupTestBase : public OobeBaseTest {
+class DemoSetupTest : public LoginManagerTest {
  public:
-  DemoSetupTestBase() = default;
-  ~DemoSetupTestBase() override = default;
+  DemoSetupTest()
+      : LoginManagerTest(false, true /* should_initialize_webui */) {}
+  ~DemoSetupTest() override = default;
+
+  // LoginTestManager:
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    LoginManagerTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitchASCII(switches::kArcAvailability,
+                                    "officially-supported");
+    ASSERT_TRUE(arc::IsArcAvailable());
+  }
 
   void SetUpOnMainThread() override {
-    OobeBaseTest::SetUpOnMainThread();
+    LoginManagerTest::SetUpOnMainThread();
     DisableConfirmationDialogAnimations();
     branded_build_override_ = WizardController::ForceBrandedBuildForTesting();
     DisconnectAllNetworks();
@@ -457,25 +466,10 @@
   policy::MockCloudPolicyStore mock_policy_store_;
   std::unique_ptr<base::AutoReset<bool>> branded_build_override_;
 
-  DISALLOW_COPY_AND_ASSIGN(DemoSetupTestBase);
+  DISALLOW_COPY_AND_ASSIGN(DemoSetupTest);
 };
 
-class DemoSetupArcSupportedTest : public DemoSetupTestBase {
- public:
-  DemoSetupArcSupportedTest() = default;
-  ~DemoSetupArcSupportedTest() override = default;
-
-  // DemoSetupTestBase:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    DemoSetupTestBase::SetUpCommandLine(command_line);
-    command_line->AppendSwitchASCII(switches::kArcAvailability,
-                                    "officially-supported");
-    ASSERT_TRUE(arc::IsArcAvailable());
-  }
-};
-
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       ShowConfirmationDialogAndProceed) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, ShowConfirmationDialogAndProceed) {
   EXPECT_FALSE(IsConfirmationDialogShown());
 
   InvokeDemoModeWithAccelerator();
@@ -487,8 +481,14 @@
   EXPECT_TRUE(IsScreenShown(DemoPreferencesScreenView::kScreenId));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       ShowConfirmationDialogAndCancel) {
+#if defined(OS_CHROMEOS)
+// Flaky on ChromeOS. crbug.com/895120
+#define MAYBE_ShowConfirmationDialogAndCancel \
+  DISABLED_ShowConfirmationDialogAndCancel
+#else
+#define MAYBE_ShowConfirmationDialogAndCancel ShowConfirmationDialogAndCancel
+#endif
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, MAYBE_ShowConfirmationDialogAndCancel) {
   EXPECT_FALSE(IsConfirmationDialogShown());
 
   InvokeDemoModeWithAccelerator();
@@ -500,7 +500,7 @@
   EXPECT_FALSE(IsScreenShown(DemoPreferencesScreenView::kScreenId));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, InvokeWithTaps) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, InvokeWithTaps) {
   // Use fake time to avoid flakiness.
   SetFakeTimeForMultiTapDetector(base::Time::UnixEpoch());
   EXPECT_FALSE(IsConfirmationDialogShown());
@@ -509,8 +509,7 @@
   EXPECT_TRUE(IsConfirmationDialogShown());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       DoNotInvokeWithNonConsecutiveTaps) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, DoNotInvokeWithNonConsecutiveTaps) {
   // Use fake time to avoid flakiness.
   const base::Time kFakeTime = base::Time::UnixEpoch();
   SetFakeTimeForMultiTapDetector(kFakeTime);
@@ -528,7 +527,7 @@
   EXPECT_FALSE(IsConfirmationDialogShown());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, OnlineSetupFlowSuccess) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OnlineSetupFlowSuccess) {
   // Simulate successful online setup.
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION);
@@ -585,7 +584,7 @@
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
+IN_PROC_BROWSER_TEST_F(DemoSetupTest,
                        OnlineSetupFlowSuccessWithCountryCustomization) {
   // Simulate successful online setup.
   enrollment_helper_.ExpectEnrollmentMode(
@@ -675,7 +674,7 @@
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, OnlineSetupFlowErrorDefault) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OnlineSetupFlowErrorDefault) {
   // Simulate online setup failure.
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION);
@@ -739,8 +738,7 @@
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       OnlineSetupFlowErrorPowerwashRequired) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OnlineSetupFlowErrorPowerwashRequired) {
   // Simulate online setup failure that requires powerwash.
   enrollment_helper_.ExpectEnrollmentMode(
       policy::EnrollmentConfig::MODE_ATTESTATION);
@@ -803,8 +801,7 @@
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       OnlineSetupFlowCrosComponentFailure) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OnlineSetupFlowCrosComponentFailure) {
   // Simulate failure to load demo resources CrOS component.
   // There is no enrollment attempt, as process fails earlier.
   enrollment_helper_.ExpectNoEnrollment();
@@ -861,7 +858,7 @@
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, OfflineDemoModeUnavailable) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineDemoModeUnavailable) {
   SimulateNetworkDisconnected();
 
   InvokeDemoModeWithAccelerator();
@@ -884,7 +881,7 @@
   EXPECT_FALSE(IsCustomNetworkListElementShown("offlineDemoSetupListItemName"));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, OfflineSetupFlowSuccess) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineSetupFlowSuccess) {
   // Simulate offline setup success.
   enrollment_helper_.ExpectOfflineEnrollmentSuccess();
   SimulateNetworkDisconnected();
@@ -941,8 +938,7 @@
   EXPECT_TRUE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       OfflineSetupFlowErrorDefault) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineSetupFlowErrorDefault) {
   // Simulate offline setup failure.
   enrollment_helper_.ExpectOfflineEnrollmentError(
       policy::EnrollmentStatus::ForStatus(
@@ -1007,8 +1003,7 @@
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       OfflineSetupFlowErrorPowerwashRequired) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineSetupFlowErrorPowerwashRequired) {
   // Simulate offline setup failure.
   enrollment_helper_.ExpectOfflineEnrollmentError(
       policy::EnrollmentStatus::ForLockError(
@@ -1072,7 +1067,7 @@
   EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, NextDisabledOnNetworkScreen) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, NextDisabledOnNetworkScreen) {
   SimulateNetworkDisconnected();
   SkipToScreen(NetworkScreenView::kScreenId);
   EXPECT_FALSE(IsScreenDialogElementEnabled(NetworkScreenView::kScreenId,
@@ -1086,7 +1081,7 @@
   EXPECT_TRUE(IsScreenShown(NetworkScreenView::kScreenId));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, ClickNetworkOnNetworkScreen) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, ClickNetworkOnNetworkScreen) {
   SkipToScreen(NetworkScreenView::kScreenId);
   EXPECT_FALSE(IsScreenDialogElementEnabled(NetworkScreenView::kScreenId,
                                             DemoSetupDialog::kNetwork,
@@ -1099,8 +1094,7 @@
   EXPECT_TRUE(IsScreenShown(EulaView::kScreenId));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       ClickConnectedNetworkOnNetworkScreen) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, ClickConnectedNetworkOnNetworkScreen) {
   SimulateNetworkConnected();
   SkipToScreen(NetworkScreenView::kScreenId);
   EXPECT_TRUE(IsScreenDialogElementEnabled(NetworkScreenView::kScreenId,
@@ -1113,7 +1107,7 @@
   EXPECT_TRUE(IsScreenShown(EulaView::kScreenId));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, BackOnNetworkScreen) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, BackOnNetworkScreen) {
   SimulateNetworkConnected();
   SkipToScreen(NetworkScreenView::kScreenId);
 
@@ -1124,7 +1118,7 @@
   EXPECT_TRUE(IsScreenShown(DemoPreferencesScreenView::kScreenId));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, BackOnArcTermsScreen) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, BackOnArcTermsScreen) {
   // User cannot go to ARC ToS screen without accepting eula - simulate that.
   StartupUtils::MarkEulaAccepted();
 
@@ -1136,7 +1130,7 @@
   OobeScreenWaiter(NetworkScreenView::kScreenId).Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, BackOnErrorScreen) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, BackOnErrorScreen) {
   SkipToErrorDialog();
 
   ClickScreenDialogButton(DemoSetupScreenView::kScreenId,
@@ -1146,7 +1140,7 @@
   OobeScreenWaiter(WelcomeView::kScreenId).Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest, RetryOnErrorScreen) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, RetryOnErrorScreen) {
   SkipToErrorDialog();
 
   // We need to create another mock after showing error dialog.
@@ -1163,8 +1157,7 @@
   OobeScreenWaiter(GaiaView::kScreenId).Wait();
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       ShowOfflineSetupOptionOnNetworkList) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, ShowOfflineSetupOptionOnNetworkList) {
   auto* const wizard_controller = WizardController::default_controller();
   wizard_controller->SimulateDemoModeSetupForTesting();
   SimulateOfflineEnvironment();
@@ -1173,20 +1166,19 @@
   EXPECT_TRUE(IsCustomNetworkListElementShown("offlineDemoSetupListItemName"));
 }
 
-IN_PROC_BROWSER_TEST_F(DemoSetupArcSupportedTest,
-                       NoOfflineSetupOptionOnNetworkList) {
+IN_PROC_BROWSER_TEST_F(DemoSetupTest, NoOfflineSetupOptionOnNetworkList) {
   SkipToScreen(NetworkScreenView::kScreenId);
   EXPECT_FALSE(IsCustomNetworkListElementShown("offlineDemoSetupListItemName"));
 }
 
-class DemoSetupArcUnsupportedTest : public DemoSetupTestBase {
+class DemoSetupArcUnsupportedTest : public DemoSetupTest {
  public:
   DemoSetupArcUnsupportedTest() = default;
   ~DemoSetupArcUnsupportedTest() override = default;
 
-  // DemoSetupTestBase:
+  // DemoSetupTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    DemoSetupTestBase::SetUpCommandLine(command_line);
+    LoginManagerTest::SetUpCommandLine(command_line);
     command_line->AppendSwitchASCII(switches::kArcAvailability, "none");
     ASSERT_FALSE(arc::IsArcAvailable());
   }
@@ -1212,7 +1204,7 @@
 }
 
 // Demo setup tests related to Force Re-Enrollment.
-class DemoSetupFRETest : public DemoSetupArcSupportedTest {
+class DemoSetupFRETest : public DemoSetupTest {
  protected:
   DemoSetupFRETest() {
     statistics_provider_.SetMachineStatistic(system::kSerialNumberKeyForTest,
@@ -1220,8 +1212,10 @@
   }
   ~DemoSetupFRETest() override = default;
 
+  void SetUpOnMainThread() override { DemoSetupTest::SetUpOnMainThread(); }
+
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    DemoSetupArcSupportedTest::SetUpCommandLine(command_line);
+    DemoSetupTest::SetUpCommandLine(command_line);
 
     command_line->AppendSwitchASCII(
         switches::kEnterpriseEnableForcedReEnrollment,
diff --git a/chrome/browser/chromeos/login/enable_debugging_browsertest.cc b/chrome/browser/chromeos/login/enable_debugging_browsertest.cc
index c0822a5..5abc8017 100644
--- a/chrome/browser/chromeos/login/enable_debugging_browsertest.cc
+++ b/chrome/browser/chromeos/login/enable_debugging_browsertest.cc
@@ -13,9 +13,9 @@
 #include "base/single_thread_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/login_manager_test.h"
 #include "chrome/browser/chromeos/login/startup_utils.h"
 #include "chrome/browser/chromeos/login/test/js_checker.h"
-#include "chrome/browser/chromeos/login/test/oobe_base_test.h"
 #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h"
 #include "chrome/browser/chromeos/login/ui/login_display_host.h"
 #include "chrome/browser/chromeos/login/ui/webui_login_view.h"
@@ -150,18 +150,21 @@
   int num_remove_protection_;
 };
 
-class EnableDebuggingTestBase : public OobeBaseTest {
+class EnableDebuggingTest : public LoginManagerTest {
  public:
-  EnableDebuggingTestBase() = default;
-  ~EnableDebuggingTestBase() override = default;
+  EnableDebuggingTest()
+      : LoginManagerTest(false, true /* should_initialize_webui */) {}
+  ~EnableDebuggingTest() override {}
 
-  // OobeBaseTest:
   void SetUpCommandLine(base::CommandLine* command_line) override {
-    OobeBaseTest::SetUpCommandLine(command_line);
+    LoginManagerTest::SetUpCommandLine(command_line);
+    command_line->AppendSwitch(chromeos::switches::kSystemDevMode);
     // Disable HID detection because it takes precedence and could block
     // enable-debugging UI.
     command_line->AppendSwitch(chromeos::switches::kDisableHIDDetectionOnOOBE);
   }
+
+  // LoginManagerTest overrides:
   void SetUpInProcessBrowserTestFixture() override {
     std::unique_ptr<DBusThreadManagerSetter> dbus_setter =
         chromeos::DBusThreadManager::GetSetterForTesting();
@@ -169,7 +172,20 @@
     dbus_setter->SetDebugDaemonClient(
         std::unique_ptr<DebugDaemonClient>(debug_daemon_client_));
 
-    OobeBaseTest::SetUpInProcessBrowserTestFixture();
+    LoginManagerTest::SetUpInProcessBrowserTestFixture();
+  }
+
+  void WaitUntilJSIsReady() {
+    LoginDisplayHost* host = LoginDisplayHost::default_host();
+    if (!host)
+      return;
+    chromeos::OobeUI* oobe_ui = host->GetOobeUI();
+    if (!oobe_ui)
+      return;
+    base::RunLoop run_loop;
+    const bool oobe_ui_ready = oobe_ui->IsJSReady(run_loop.QuitClosure());
+    if (!oobe_ui_ready)
+      run_loop.Run();
   }
 
   void InvokeEnableDebuggingScreen() {
@@ -208,7 +224,7 @@
   void ShowRemoveProtectionScreen() {
     debug_daemon_client_->SetDebuggingFeaturesStatus(
         DebugDaemonClient::DEV_FEATURE_NONE);
-    OobeBaseTest::WaitForOobeUI();
+    WaitUntilJSIsReady();
     test::OobeJS().ExpectHidden("debugging");
     InvokeEnableDebuggingScreen();
     test::OobeJS().ExpectVisible("debugging");
@@ -229,7 +245,7 @@
   void ShowSetupScreen() {
     debug_daemon_client_->SetDebuggingFeaturesStatus(
         debugd::DevFeatureFlag::DEV_FEATURE_ROOTFS_VERIFICATION_REMOVED);
-    OobeBaseTest::WaitForOobeUI();
+    WaitUntilJSIsReady();
     test::OobeJS().ExpectHidden("debugging");
     InvokeEnableDebuggingScreen();
     test::OobeJS().ExpectVisible("debugging");
@@ -250,23 +266,11 @@
   TestDebugDaemonClient* debug_daemon_client_ = nullptr;
 
  private:
-  DISALLOW_COPY_AND_ASSIGN(EnableDebuggingTestBase);
-};
-
-class EnableDebuggingDevTest : public EnableDebuggingTestBase {
- public:
-  EnableDebuggingDevTest() = default;
-  ~EnableDebuggingDevTest() override = default;
-
-  // EnableDebuggingTestBase:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    EnableDebuggingTestBase::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(chromeos::switches::kSystemDevMode);
-  }
+  DISALLOW_COPY_AND_ASSIGN(EnableDebuggingTest);
 };
 
 // Show remove protection screen, click on [Cancel] button.
-IN_PROC_BROWSER_TEST_F(EnableDebuggingDevTest, ShowAndCancelRemoveProtection) {
+IN_PROC_BROWSER_TEST_F(EnableDebuggingTest, ShowAndCancelRemoveProtection) {
   ShowRemoveProtectionScreen();
   CloseEnableDebuggingScreen();
   test::OobeJS().ExpectHidden("debugging");
@@ -278,7 +282,7 @@
 
 // Show remove protection, click on [Remove protection] button and wait for
 // reboot.
-IN_PROC_BROWSER_TEST_F(EnableDebuggingDevTest, ShowAndRemoveProtection) {
+IN_PROC_BROWSER_TEST_F(EnableDebuggingTest, ShowAndRemoveProtection) {
   ShowRemoveProtectionScreen();
   debug_daemon_client_->ResetWait();
   ClickRemoveProtectionButton();
@@ -293,7 +297,7 @@
 }
 
 // Show setup screen. Click on [Enable] button. Wait until done screen is shown.
-IN_PROC_BROWSER_TEST_F(EnableDebuggingDevTest, ShowSetup) {
+IN_PROC_BROWSER_TEST_F(EnableDebuggingTest, ShowSetup) {
   ShowSetupScreen();
   debug_daemon_client_->ResetWait();
   ClickEnableButton();
@@ -305,7 +309,7 @@
 
 // Show setup screen. Type in matching passwords.
 // Click on [Enable] button. Wait until done screen is shown.
-IN_PROC_BROWSER_TEST_F(EnableDebuggingDevTest, SetupMatchingPasswords) {
+IN_PROC_BROWSER_TEST_F(EnableDebuggingTest, SetupMatchingPasswords) {
   ShowSetupScreen();
   debug_daemon_client_->ResetWait();
   test::OobeJS().TypeIntoPath("test0000", {"enable-debugging-password"});
@@ -321,7 +325,7 @@
 // Show setup screen. Type in different passwords.
 // Click on [Enable] button. Assert done screen is not shown.
 // Then confirm that typing in matching passwords enables debugging features.
-IN_PROC_BROWSER_TEST_F(EnableDebuggingDevTest, SetupNotMatchingPasswords) {
+IN_PROC_BROWSER_TEST_F(EnableDebuggingTest, SetupNotMatchingPasswords) {
   ShowSetupScreen();
   debug_daemon_client_->ResetWait();
   test::OobeJS().TypeIntoPath("test0000", {"enable-debugging-password"});
@@ -345,11 +349,11 @@
 
 // Test images come with some features enabled but still has rootfs protection.
 // Invoking debug screen should show remove protection screen.
-IN_PROC_BROWSER_TEST_F(EnableDebuggingDevTest, ShowOnTestImages) {
+IN_PROC_BROWSER_TEST_F(EnableDebuggingTest, ShowOnTestImages) {
   debug_daemon_client_->SetDebuggingFeaturesStatus(
       debugd::DevFeatureFlag::DEV_FEATURE_SSH_SERVER_CONFIGURED |
       debugd::DevFeatureFlag::DEV_FEATURE_SYSTEM_ROOT_PASSWORD_SET);
-  OobeBaseTest::WaitForOobeUI();
+  WaitUntilJSIsReady();
   test::OobeJS().ExpectHidden("debugging");
   InvokeEnableDebuggingScreen();
   test::OobeJS().ExpectVisible("debugging");
@@ -362,12 +366,12 @@
   EXPECT_EQ(debug_daemon_client_->num_remove_protection(), 0);
 }
 
-IN_PROC_BROWSER_TEST_F(EnableDebuggingDevTest, WaitForDebugDaemon) {
+IN_PROC_BROWSER_TEST_F(EnableDebuggingTest, WaitForDebugDaemon) {
   // Stat with service not ready.
   debug_daemon_client_->SetServiceIsAvailable(false);
   debug_daemon_client_->SetDebuggingFeaturesStatus(
       DebugDaemonClient::DEV_FEATURE_NONE);
-  OobeBaseTest::WaitForOobeUI();
+  WaitUntilJSIsReady();
 
   // Invoking UI and it should land on wait-view.
   test::OobeJS().ExpectHidden("debugging");
@@ -382,16 +386,22 @@
   VerifyRemoveProtectionScreen();
 }
 
-class EnableDebuggingNonDevTest : public EnableDebuggingTestBase {
+class EnableDebuggingNonDevTest : public EnableDebuggingTest {
  public:
-  EnableDebuggingNonDevTest() = default;
+  EnableDebuggingNonDevTest() {}
 
+  void SetUpCommandLine(base::CommandLine* command_line) override {
+    // Skip EnableDebuggingTest::SetUpCommandLine().
+    LoginManagerTest::SetUpCommandLine(command_line);
+  }
+
+  // LoginManagerTest overrides:
   void SetUpInProcessBrowserTestFixture() override {
     std::unique_ptr<DBusThreadManagerSetter> dbus_setter =
         chromeos::DBusThreadManager::GetSetterForTesting();
     dbus_setter->SetDebugDaemonClient(
         std::unique_ptr<DebugDaemonClient>(new FakeDebugDaemonClient));
-    EnableDebuggingTestBase::SetUpInProcessBrowserTestFixture();
+    LoginManagerTest::SetUpInProcessBrowserTestFixture();
   }
 };
 
@@ -409,11 +419,11 @@
   test::OobeJS().ExpectHasNoClass("wait-view", {"debugging"});
 }
 
-class EnableDebuggingRequestedTest : public EnableDebuggingDevTest {
+class EnableDebuggingRequestedTest : public EnableDebuggingTest {
  public:
   EnableDebuggingRequestedTest() {}
 
-  // EnableDebuggingDevTest overrides:
+  // EnableDebuggingTest overrides:
   bool SetUpUserDataDirectory() override {
     base::DictionaryValue local_state_dict;
     local_state_dict.SetBoolean(prefs::kDebuggingFeaturesRequested, true);
@@ -425,10 +435,10 @@
     CHECK(
         JSONFileValueSerializer(local_state_path).Serialize(local_state_dict));
 
-    return EnableDebuggingDevTest::SetUpUserDataDirectory();
+    return EnableDebuggingTest::SetUpUserDataDirectory();
   }
   void SetUpInProcessBrowserTestFixture() override {
-    EnableDebuggingDevTest::SetUpInProcessBrowserTestFixture();
+    EnableDebuggingTest::SetUpInProcessBrowserTestFixture();
 
     debug_daemon_client_->SetDebuggingFeaturesStatus(
         debugd::DevFeatureFlag::DEV_FEATURE_ROOTFS_VERIFICATION_REMOVED);
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
index 572f473..5ce1e1d 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.cc
@@ -103,6 +103,16 @@
 namespace chromeos {
 
 // static
+std::string EnrollmentScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::COMPLETED:
+      return "Completed";
+    case Result::BACK:
+      return "Back";
+  }
+}
+
+// static
 EnrollmentScreen* EnrollmentScreen::Get(ScreenManager* manager) {
   return static_cast<EnrollmentScreen*>(
       manager->GetScreen(EnrollmentScreenView::kScreenId));
diff --git a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
index fb2d6b25..2f47e03 100644
--- a/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
+++ b/chrome/browser/chromeos/login/enrollment/enrollment_screen.h
@@ -46,6 +46,8 @@
  public:
   enum class Result { COMPLETED, BACK };
 
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   EnrollmentScreen(EnrollmentScreenView* view,
                    const ScreenExitCallback& exit_callback);
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
index a62d1b2..3143dd2 100644
--- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.cc
@@ -25,6 +25,18 @@
 namespace chromeos {
 
 // static
+std::string ArcTermsOfServiceScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::ACCEPTED:
+      return "Accepted";
+    case Result::SKIPPED:
+      return "Skipped";
+    case Result::BACK:
+      return "Back";
+  }
+}
+
+// static
 void ArcTermsOfServiceScreen::MaybeLaunchArcSettings(Profile* profile) {
   if (profile->GetPrefs()->GetBoolean(prefs::kShowArcSettingsOnSessionStart)) {
     profile->GetPrefs()->ClearPref(prefs::kShowArcSettingsOnSessionStart);
diff --git a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
index e2195e6b..67329db 100644
--- a/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
+++ b/chrome/browser/chromeos/login/screens/arc_terms_of_service_screen.h
@@ -23,6 +23,8 @@
  public:
   enum class Result { ACCEPTED, SKIPPED, BACK };
 
+  static std::string GetResultString(Result result);
+
   // Launches the ARC settings page if the user requested to review them after
   // completing OOBE.
   static void MaybeLaunchArcSettings(Profile* profile);
diff --git a/chrome/browser/chromeos/login/screens/demo_preferences_screen.cc b/chrome/browser/chromeos/login/screens/demo_preferences_screen.cc
index f89ea24..43f367d8 100644
--- a/chrome/browser/chromeos/login/screens/demo_preferences_screen.cc
+++ b/chrome/browser/chromeos/login/screens/demo_preferences_screen.cc
@@ -37,6 +37,16 @@
 
 }  // namespace
 
+// static
+std::string DemoPreferencesScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::COMPLETED:
+      return "Completed";
+    case Result::CANCELED:
+      return "Canceled";
+  }
+}
+
 DemoPreferencesScreen::DemoPreferencesScreen(
     DemoPreferencesScreenView* view,
     const ScreenExitCallback& exit_callback)
diff --git a/chrome/browser/chromeos/login/screens/demo_preferences_screen.h b/chrome/browser/chromeos/login/screens/demo_preferences_screen.h
index 6b36a8a..92c75a3 100644
--- a/chrome/browser/chromeos/login/screens/demo_preferences_screen.h
+++ b/chrome/browser/chromeos/login/screens/demo_preferences_screen.h
@@ -25,6 +25,8 @@
  public:
   enum class Result { COMPLETED, CANCELED };
 
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   DemoPreferencesScreen(DemoPreferencesScreenView* view,
                         const ScreenExitCallback& exit_callback);
diff --git a/chrome/browser/chromeos/login/screens/demo_setup_screen.cc b/chrome/browser/chromeos/login/screens/demo_setup_screen.cc
index 45d07f48..f865c8baf 100644
--- a/chrome/browser/chromeos/login/screens/demo_setup_screen.cc
+++ b/chrome/browser/chromeos/login/screens/demo_setup_screen.cc
@@ -19,6 +19,16 @@
 
 namespace chromeos {
 
+// static
+std::string DemoSetupScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::COMPLETED:
+      return "Completed";
+    case Result::CANCELED:
+      return "Canceled";
+  }
+}
+
 DemoSetupScreen::DemoSetupScreen(DemoSetupScreenView* view,
                                  const ScreenExitCallback& exit_callback)
     : BaseScreen(DemoSetupScreenView::kScreenId, OobeScreenPriority::DEFAULT),
diff --git a/chrome/browser/chromeos/login/screens/demo_setup_screen.h b/chrome/browser/chromeos/login/screens/demo_setup_screen.h
index 78293fc..1327c97 100644
--- a/chrome/browser/chromeos/login/screens/demo_setup_screen.h
+++ b/chrome/browser/chromeos/login/screens/demo_setup_screen.h
@@ -23,6 +23,8 @@
  public:
   enum class Result { COMPLETED, CANCELED };
 
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   DemoSetupScreen(DemoSetupScreenView* view,
                   const ScreenExitCallback& exit_callback);
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.cc b/chrome/browser/chromeos/login/screens/eula_screen.cc
index 84925a7..ed4c199 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.cc
+++ b/chrome/browser/chromeos/login/screens/eula_screen.cc
@@ -29,6 +29,18 @@
 
 }  // namespace
 
+// static
+std::string EulaScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::ACCEPTED_WITH_USAGE_STATS_REPORTING:
+      return "AcceptedWithStats";
+    case Result::ACCEPTED_WITHOUT_USAGE_STATS_REPORTING:
+      return "AcceptedWithoutStats";
+    case Result::BACK:
+      return "Back";
+  }
+}
+
 EulaScreen::EulaScreen(EulaView* view, const ScreenExitCallback& exit_callback)
     : BaseScreen(EulaView::kScreenId, OobeScreenPriority::DEFAULT),
       view_(view),
diff --git a/chrome/browser/chromeos/login/screens/eula_screen.h b/chrome/browser/chromeos/login/screens/eula_screen.h
index 8148d3d..c518dc3 100644
--- a/chrome/browser/chromeos/login/screens/eula_screen.h
+++ b/chrome/browser/chromeos/login/screens/eula_screen.h
@@ -31,6 +31,8 @@
     BACK
   };
 
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   EulaScreen(EulaView* view, const ScreenExitCallback& exit_callback);
   ~EulaScreen() override;
diff --git a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.cc b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.cc
index 828ab7d..3bb5b63 100644
--- a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.cc
+++ b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.cc
@@ -11,6 +11,16 @@
 
 namespace chromeos {
 
+// static
+std::string KioskAutolaunchScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::COMPLETED:
+      return "Completed";
+    case Result::CANCELED:
+      return "Canceled";
+  }
+}
+
 KioskAutolaunchScreen::KioskAutolaunchScreen(
     KioskAutolaunchScreenView* view,
     const ScreenExitCallback& exit_callback)
diff --git a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h
index 641dda8a..5a6eb29 100644
--- a/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h
+++ b/chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h
@@ -22,6 +22,8 @@
  public:
   enum class Result { COMPLETED, CANCELED };
 
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   KioskAutolaunchScreen(KioskAutolaunchScreenView* view,
                         const ScreenExitCallback& exit_callback);
diff --git a/chrome/browser/chromeos/login/screens/network_screen.cc b/chrome/browser/chromeos/login/screens/network_screen.cc
index 88f896b..b3900b4 100644
--- a/chrome/browser/chromeos/login/screens/network_screen.cc
+++ b/chrome/browser/chromeos/login/screens/network_screen.cc
@@ -31,6 +31,18 @@
 namespace chromeos {
 
 // static
+std::string NetworkScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::CONNECTED:
+      return "Connected";
+    case Result::OFFLINE_DEMO_SETUP:
+      return "OfflineDemoSetup";
+    case Result::BACK:
+      return "Back";
+  }
+}
+
+// static
 NetworkScreen* NetworkScreen::Get(ScreenManager* manager) {
   return static_cast<NetworkScreen*>(
       manager->GetScreen(NetworkScreenView::kScreenId));
diff --git a/chrome/browser/chromeos/login/screens/network_screen.h b/chrome/browser/chromeos/login/screens/network_screen.h
index 6e2d6b3..50139dc 100644
--- a/chrome/browser/chromeos/login/screens/network_screen.h
+++ b/chrome/browser/chromeos/login/screens/network_screen.h
@@ -31,6 +31,8 @@
  public:
   enum class Result { CONNECTED, OFFLINE_DEMO_SETUP, BACK };
 
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   NetworkScreen(NetworkScreenView* view,
                 const ScreenExitCallback& exit_callback);
diff --git a/chrome/browser/chromeos/login/screens/packaged_license_screen.cc b/chrome/browser/chromeos/login/screens/packaged_license_screen.cc
index 5cbea037..aa89cdb 100644
--- a/chrome/browser/chromeos/login/screens/packaged_license_screen.cc
+++ b/chrome/browser/chromeos/login/screens/packaged_license_screen.cc
@@ -15,6 +15,16 @@
 
 namespace chromeos {
 
+// static
+std::string PackagedLicenseScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::DONT_ENROLL:
+      return "DontEnroll";
+    case Result::ENROLL:
+      return "Enroll";
+  }
+}
+
 PackagedLicenseScreen::PackagedLicenseScreen(
     PackagedLicenseView* view,
     const ScreenExitCallback& exit_callback)
diff --git a/chrome/browser/chromeos/login/screens/packaged_license_screen.h b/chrome/browser/chromeos/login/screens/packaged_license_screen.h
index 2465714..f2ba3a31 100644
--- a/chrome/browser/chromeos/login/screens/packaged_license_screen.h
+++ b/chrome/browser/chromeos/login/screens/packaged_license_screen.h
@@ -25,6 +25,9 @@
     // Show enterprise enrollment screen
     ENROLL
   };
+
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   PackagedLicenseScreen(PackagedLicenseView* view,
                         const ScreenExitCallback& exit_callback);
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc b/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc
index 47acbf8..ad7aabcb 100644
--- a/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc
+++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen.cc
@@ -9,6 +9,16 @@
 
 namespace chromeos {
 
+// static
+std::string RecommendAppsScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::SELECTED:
+      return "Selected";
+    case Result::SKIPPED:
+      return "Skipped";
+  }
+}
+
 RecommendAppsScreen::RecommendAppsScreen(
     RecommendAppsScreenView* view,
     const ScreenExitCallback& exit_callback)
diff --git a/chrome/browser/chromeos/login/screens/recommend_apps_screen.h b/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
index c54f7cff..0c6557c 100644
--- a/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
+++ b/chrome/browser/chromeos/login/screens/recommend_apps_screen.h
@@ -29,6 +29,8 @@
  public:
   enum class Result { SELECTED, SKIPPED };
 
+  static std::string GetResultString(Result result);
+
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
   RecommendAppsScreen(RecommendAppsScreenView* view,
                       const ScreenExitCallback& exit_callback);
diff --git a/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc b/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc
index 386d0bc..9e1c09d 100644
--- a/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc
+++ b/chrome/browser/chromeos/login/screens/terms_of_service_screen.cc
@@ -36,6 +36,16 @@
 
 }  // namespace
 
+// static
+std::string TermsOfServiceScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::ACCEPTED:
+      return "Accepted";
+    case Result::DECLINED:
+      return "Declined";
+  }
+}
+
 TermsOfServiceScreen::TermsOfServiceScreen(
     TermsOfServiceScreenView* view,
     const ScreenExitCallback& exit_callback)
diff --git a/chrome/browser/chromeos/login/screens/terms_of_service_screen.h b/chrome/browser/chromeos/login/screens/terms_of_service_screen.h
index 78afcd2..6908f2c 100644
--- a/chrome/browser/chromeos/login/screens/terms_of_service_screen.h
+++ b/chrome/browser/chromeos/login/screens/terms_of_service_screen.h
@@ -29,6 +29,8 @@
  public:
   enum class Result { ACCEPTED, DECLINED };
 
+  static std::string GetResultString(Result result);
+
   // The possible states that the screen may assume.
   enum class ScreenState : int { LOADING = 0, LOADED = 1, ERROR = 2 };
 
diff --git a/chrome/browser/chromeos/login/screens/update_screen.cc b/chrome/browser/chromeos/login/screens/update_screen.cc
index 0e4e808..615417e1 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.cc
+++ b/chrome/browser/chromeos/login/screens/update_screen.cc
@@ -42,6 +42,16 @@
 }  // anonymous namespace
 
 // static
+std::string UpdateScreen::GetResultString(Result result) {
+  switch (result) {
+    case Result::UPDATE_NOT_REQUIRED:
+      return "UpdateNotRequired";
+    case Result::UPDATE_ERROR:
+      return "UpdateError";
+  }
+}
+
+// static
 UpdateScreen* UpdateScreen::Get(ScreenManager* manager) {
   return static_cast<UpdateScreen*>(manager->GetScreen(UpdateView::kScreenId));
 }
diff --git a/chrome/browser/chromeos/login/screens/update_screen.h b/chrome/browser/chromeos/login/screens/update_screen.h
index ecce6de..9d8984c3 100644
--- a/chrome/browser/chromeos/login/screens/update_screen.h
+++ b/chrome/browser/chromeos/login/screens/update_screen.h
@@ -57,6 +57,8 @@
  public:
   using Result = VersionUpdater::Result;
 
+  static std::string GetResultString(Result result);
+
   static UpdateScreen* Get(ScreenManager* manager);
 
   using ScreenExitCallback = base::RepeatingCallback<void(Result result)>;
diff --git a/chrome/browser/chromeos/login/signin/device_id_browsertest.cc b/chrome/browser/chromeos/login/signin/device_id_browsertest.cc
index 13eb6a7..a7cdfe6 100644
--- a/chrome/browser/chromeos/login/signin/device_id_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/device_id_browsertest.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 "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
 #include "base/json/json_reader.h"
@@ -24,7 +24,6 @@
 #include "components/prefs/pref_service.h"
 #include "components/signin/public/base/signin_pref_names.h"
 #include "components/user_manager/known_user.h"
-#include "components/user_manager/remove_user_delegate.h"
 #include "components/user_manager/user_manager.h"
 
 namespace {
@@ -45,21 +44,22 @@
 namespace chromeos {
 
 class DeviceIDTest : public OobeBaseTest,
-                     public user_manager::RemoveUserDelegate {
+                     public user_manager::UserManager::Observer {
  public:
   void SetUpCommandLine(base::CommandLine* command_line) override {
     OobeBaseTest::SetUpCommandLine(command_line);
     command_line->AppendSwitch(switches::kOobeSkipPostLogin);
-    command_line->AppendSwitch(ash::switches::kShowWebUiLogin);
   }
 
   void SetUpOnMainThread() override {
     user_removal_loop_.reset(new base::RunLoop);
     OobeBaseTest::SetUpOnMainThread();
     LoadRefreshTokenToDeviceIdMap();
+    user_manager::UserManager::Get()->AddObserver(this);
   }
 
   void TearDownOnMainThread() override {
+    user_manager::UserManager::Get()->RemoveObserver(this);
     SaveRefreshTokenToDeviceIdMap();
     OobeBaseTest::TearDownOnMainThread();
   }
@@ -117,24 +117,19 @@
   }
 
   void SignInOffline(const std::string& user_id, const std::string& password) {
-    WaitForSigninScreen();
-
-    test::OobeJS().ExecuteAsync(base::StringPrintf(
-        "chrome.send('authenticateUser', ['%s', '%s', false])", user_id.c_str(),
-        password.c_str()));
+    ash::LoginScreenTestApi::SubmitPassword(AccountId::FromUserEmail(user_id),
+                                            FakeGaiaMixin::kFakeUserPassword,
+                                            false /* check_if_submittable */);
     test::WaitForPrimaryUserSessionStart();
   }
 
   void RemoveUser(const AccountId& account_id) {
-    user_manager::UserManager::Get()->RemoveUser(account_id, this);
+    ASSERT_TRUE(ash::LoginScreenTestApi::RemoveUser(account_id));
     user_removal_loop_->Run();
   }
 
  private:
-  // user_manager::RemoveUserDelegate:
-  void OnBeforeUserRemoved(const AccountId& account_id) override {}
-
-  void OnUserRemoved(const AccountId& account_id) override {
+  void LocalStateChanged(user_manager::UserManager* manager) override {
     user_removal_loop_->Quit();
   }
 
@@ -195,6 +190,7 @@
   EXPECT_FALSE(device_id.empty());
   EXPECT_EQ(device_id, GetDeviceIdFromGAIA(kRefreshToken1));
 
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
   SignInOnline(FakeGaiaMixin::kFakeUserEmail, FakeGaiaMixin::kFakeUserPassword,
                kRefreshToken2, FakeGaiaMixin::kFakeUserGaiaId);
   CheckDeviceIDIsConsistent(
@@ -225,8 +221,7 @@
 
 // Add the second user.
 IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_PRE_NewUsers) {
-  WaitForSigninScreen();
-  test::OobeJS().ExecuteAsync("chrome.send('showAddUser')");
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
   SignInOnline(kSecondUserEmail, kSecondUserPassword, kSecondUserRefreshToken1,
                kSecondUserGaiaId);
   CheckDeviceIDIsConsistent(AccountId::FromUserEmail(kSecondUserEmail),
@@ -235,13 +230,13 @@
 
 // Remove the second user.
 IN_PROC_BROWSER_TEST_F(DeviceIDTest, PRE_NewUsers) {
-  WaitForSigninScreen();
   RemoveUser(AccountId::FromUserEmail(kSecondUserEmail));
 }
 
 // Add the second user back. Verify that device ID has been changed.
 IN_PROC_BROWSER_TEST_F(DeviceIDTest, NewUsers) {
   EXPECT_TRUE(GetDeviceId(AccountId::FromUserEmail(kSecondUserEmail)).empty());
+  ASSERT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton());
   SignInOnline(kSecondUserEmail, kSecondUserPassword, kSecondUserRefreshToken2,
                kSecondUserGaiaId);
   CheckDeviceIDIsConsistent(AccountId::FromUserEmail(kSecondUserEmail),
diff --git a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
index 1d992f7e..16de156f 100644
--- a/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
+++ b/chrome/browser/chromeos/login/signin/oauth2_browsertest.cc
@@ -7,7 +7,7 @@
 #include <string>
 #include <utility>
 
-#include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/bind.h"
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
@@ -244,7 +244,6 @@
   // OobeBaseTest overrides.
   void SetUpCommandLine(base::CommandLine* command_line) override {
     OobeBaseTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(ash::switches::kShowWebUiLogin);
 
     base::PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir_);
 
@@ -304,9 +303,6 @@
   }
 
   void LoginAsExistingUser() {
-    test::OobeJS().ExpectTrue("!!document.querySelector('#account-picker')");
-    test::OobeJS().ExpectTrue("!!document.querySelector('#pod-row')");
-
     // PickAccountId does not work at this point as the primary user profile has
     // not yet been created.
     const std::string email = kTestEmail;
@@ -314,9 +310,10 @@
               user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
 
     // Try login.  Primary profile has changed.
-    EXPECT_TRUE(
-        TryToLogin(AccountId::FromUserEmailGaiaId(kTestEmail, kTestGaiaId),
-                   kTestAccountPassword));
+    ash::LoginScreenTestApi::SubmitPassword(
+        AccountId::FromUserEmailGaiaId(kTestEmail, kTestGaiaId),
+        kTestAccountPassword, true /*check_if_submittable */);
+    test::WaitForPrimaryUserSessionStart();
     Profile* profile = ProfileManager::GetPrimaryUserProfile();
     CoreAccountId account_id = PickAccountId(profile, kTestGaiaId, kTestEmail);
     ASSERT_EQ(email, account_id.ToString());
@@ -568,8 +565,7 @@
 IN_PROC_BROWSER_TEST_F(OAuth2Test, MergeSession) {
   SimulateNetworkOnline();
 
-  test::OobeJS().ExpectTrue("!!document.querySelector('#account-picker')");
-  test::OobeJS().ExpectTrue("!!document.querySelector('#pod-row')");
+  EXPECT_EQ(1, ash::LoginScreenTestApi::GetUsersCount());
 
   // PickAccountId does not work at this point as the primary user profile has
   // not yet been created.
diff --git a/chrome/browser/chromeos/login/test/js_checker.cc b/chrome/browser/chromeos/login/test/js_checker.cc
index e8f4304..2c667fe90 100644
--- a/chrome/browser/chromeos/login/test/js_checker.cc
+++ b/chrome/browser/chromeos/login/test/js_checker.cc
@@ -45,6 +45,18 @@
   return js;
 }
 
+std::string DescribePath(std::initializer_list<base::StringPiece> element_ids) {
+  CHECK(element_ids.size() > 0);
+  std::string result;
+  std::initializer_list<base::StringPiece>::const_iterator it =
+      element_ids.begin();
+  result.append("//").append(std::string(*it));
+  for (it++; it < element_ids.end(); it++) {
+    result.append("/").append(std::string(*it));
+  }
+  return result;
+}
+
 }  // namespace
 
 namespace chromeos {
@@ -131,6 +143,16 @@
   return std::make_unique<TestPredicateWaiter>(predicate);
 }
 
+std::unique_ptr<TestConditionWaiter> JSChecker::CreateWaiterWithDescription(
+    const std::string& js_condition,
+    const std::string& description) {
+  TestPredicateWaiter::PredicateCheck predicate = base::BindRepeating(
+      &CheckOobeCondition, base::Unretained(web_contents_), js_condition);
+  auto result = std::make_unique<TestPredicateWaiter>(predicate);
+  result->set_description(description);
+  return result;
+}
+
 std::unique_ptr<TestConditionWaiter> JSChecker::CreateAttributePresenceWaiter(
     const std::string& attribute,
     bool presence,
@@ -139,7 +161,13 @@
   if (!presence) {
     condition = "!(" + condition + ")";
   }
-  return CreateWaiter(condition);
+  std::string description;
+  description.append("Attribute ")
+      .append(attribute)
+      .append(presence ? " present " : " absent ")
+      .append("for ")
+      .append(DescribePath(element_ids));
+  return CreateWaiterWithDescription(condition, description);
 }
 
 std::unique_ptr<TestConditionWaiter> JSChecker::CreateVisibilityWaiter(
@@ -155,7 +183,9 @@
   if (visibility) {
     js_condition = "!(" + js_condition + ")";
   }
-  return CreateWaiter(js_condition);
+  std::string description;
+  description.append(element).append(visibility ? " visible" : " hidden");
+  return CreateWaiterWithDescription(js_condition, description);
 }
 
 std::unique_ptr<TestConditionWaiter> JSChecker::CreateDisplayedWaiter(
@@ -167,7 +197,10 @@
   if (!displayed) {
     js_condition = "!(" + js_condition + ")";
   }
-  return CreateWaiter(js_condition);
+  std::string description;
+  description.append(DescribePath(element_ids))
+      .append(displayed ? " displayed" : " not displayed");
+  return CreateWaiterWithDescription(js_condition, description);
 }
 
 std::unique_ptr<TestConditionWaiter> JSChecker::CreateEnabledWaiter(
@@ -177,7 +210,10 @@
   if (enabled) {
     js_condition = "!(" + js_condition + ")";
   }
-  return CreateWaiter(js_condition);
+  std::string description;
+  description.append(DescribePath(element_ids))
+      .append(enabled ? " enabled" : " disabled");
+  return CreateWaiterWithDescription(js_condition, description);
 }
 
 std::unique_ptr<TestConditionWaiter> JSChecker::CreateHasClassWaiter(
@@ -188,7 +224,13 @@
   if (!has_class) {
     js_condition = "!(" + js_condition + ")";
   }
-  return CreateWaiter(js_condition);
+  std::string description;
+  description.append(DescribePath(element_ids))
+      .append(" has ")
+      .append(has_class ? "no " : "")
+      .append(" css class ")
+      .append(css_class);
+  return CreateWaiterWithDescription(js_condition, description);
 }
 
 void JSChecker::GetBoolImpl(const std::string& expression, bool* result) {
@@ -400,7 +442,8 @@
     const std::string& oobe_screen_id) {
   std::string js = "Oobe.getInstance().currentScreen.id=='$ScreenId'";
   base::ReplaceSubstringsAfterOffset(&js, 0, "$ScreenId", oobe_screen_id);
-  return test::OobeJS().CreateWaiter(js);
+  std::string description = "OOBE Screen is " + oobe_screen_id;
+  return test::OobeJS().CreateWaiterWithDescription(js, description);
 }
 
 }  // namespace test
diff --git a/chrome/browser/chromeos/login/test/js_checker.h b/chrome/browser/chromeos/login/test/js_checker.h
index 78e72f8..bef49c44 100644
--- a/chrome/browser/chromeos/login/test/js_checker.h
+++ b/chrome/browser/chromeos/login/test/js_checker.h
@@ -61,6 +61,12 @@
   WARN_UNUSED_RESULT std::unique_ptr<TestConditionWaiter> CreateWaiter(
       const std::string& js_condition);
 
+  // Checks test waiter that would await until |js_condition| evaluates
+  // to true.
+  WARN_UNUSED_RESULT std::unique_ptr<TestConditionWaiter>
+  CreateWaiterWithDescription(const std::string& js_condition,
+                              const std::string& description);
+
   // Waiter that waits until the given attribute is (not) present.
   // WARNING! This does not cover the case where ATTRIBUTE=false.
   // Should only be used for boolean attributes.
diff --git a/chrome/browser/chromeos/login/test/oobe_screen_exit_waiter.cc b/chrome/browser/chromeos/login/test/oobe_screen_exit_waiter.cc
index 0765cbe..605f91e 100644
--- a/chrome/browser/chromeos/login/test/oobe_screen_exit_waiter.cc
+++ b/chrome/browser/chromeos/login/test/oobe_screen_exit_waiter.cc
@@ -29,6 +29,8 @@
 
   state_ = State::WAITING_FOR_SCREEN_EXIT;
 
+  LOG(INFO) << "Actually waiting for exiting screen " << target_screen_.name;
+
   run_loop_ = std::make_unique<base::RunLoop>();
   run_loop_->Run();
   run_loop_.reset();
diff --git a/chrome/browser/chromeos/login/test/oobe_screen_waiter.cc b/chrome/browser/chromeos/login/test/oobe_screen_waiter.cc
index 9f22009..76e8d1b 100644
--- a/chrome/browser/chromeos/login/test/oobe_screen_waiter.cc
+++ b/chrome/browser/chromeos/login/test/oobe_screen_waiter.cc
@@ -38,6 +38,8 @@
 
   state_ = State::WAITING_FOR_SCREEN;
 
+  LOG(INFO) << "Actually waiting for screen " << target_screen_.name;
+
   run_loop_ = std::make_unique<base::RunLoop>();
   run_loop_->Run();
   run_loop_.reset();
diff --git a/chrome/browser/chromeos/login/test/test_predicate_waiter.cc b/chrome/browser/chromeos/login/test/test_predicate_waiter.cc
index 53edb3f..87d4b41 100644
--- a/chrome/browser/chromeos/login/test/test_predicate_waiter.cc
+++ b/chrome/browser/chromeos/login/test/test_predicate_waiter.cc
@@ -25,6 +25,10 @@
   if (is_fulfilled_.Run())
     return;
 
+  if (!description_.empty()) {
+    LOG(INFO) << "Actually waiting for " << description_;
+  }
+
   timer_.Start(FROM_HERE, kPredicateCheckFrequency, this,
                &TestPredicateWaiter::CheckPredicate);
   run_loop_.Run();
diff --git a/chrome/browser/chromeos/login/test/test_predicate_waiter.h b/chrome/browser/chromeos/login/test/test_predicate_waiter.h
index 5d7bbec..589ce38 100644
--- a/chrome/browser/chromeos/login/test/test_predicate_waiter.h
+++ b/chrome/browser/chromeos/login/test/test_predicate_waiter.h
@@ -21,6 +21,10 @@
   explicit TestPredicateWaiter(const PredicateCheck& is_fulfilled);
   ~TestPredicateWaiter() override;
 
+  void set_description(const std::string& description) {
+    description_ = description;
+  }
+
   // TestConditionWaiter
   void Wait() override;
 
@@ -31,6 +35,7 @@
 
   base::RetainingOneShotTimer timer_;
   base::RunLoop run_loop_;
+  std::string description_;
 
   DISALLOW_COPY_AND_ASSIGN(TestPredicateWaiter);
 };
diff --git a/chrome/browser/chromeos/login/wizard_controller.cc b/chrome/browser/chromeos/login/wizard_controller.cc
index eb87f7c..8c61ab0 100644
--- a/chrome/browser/chromeos/login/wizard_controller.cc
+++ b/chrome/browser/chromeos/login/wizard_controller.cc
@@ -179,6 +179,9 @@
 // Total timezone resolving process timeout.
 const unsigned int kResolveTimeZoneTimeoutSeconds = 60;
 
+constexpr const char kDefaultExitReason[] = "Next";
+constexpr const char kResetScreenExitReason[] = "Cancel";
+
 // Stores the list of all screens that should be shown when resuming OOBE.
 const chromeos::StaticOobeScreenId kResumableScreens[] = {
     chromeos::WelcomeView::kScreenId,
@@ -254,6 +257,7 @@
     {chromeos::TermsOfServiceScreenView::kScreenId, "tos"}};
 
 void RecordUMAHistogramForOOBEStepCompletionTime(chromeos::OobeScreenId screen,
+                                                 const std::string& exit_reason,
                                                  base::TimeDelta step_time) {
   // Fetch screen name; make sure to use initial UMA name if the name has
   // changed.
@@ -276,6 +280,17 @@
       base::TimeDelta::FromMinutes(3), 50,
       base::HistogramBase::kUmaTargetedHistogramFlag);
   histogram->AddTime(step_time);
+
+  // Use for this Histogram real screen names.
+  screen_name = screen.name;
+  screen_name[0] = std::toupper(screen_name[0]);
+  std::string histogram_name_with_reason =
+      "OOBE.StepCompletionTimeByExitReason." + screen_name + "." + exit_reason;
+  base::HistogramBase* histogram_with_reason = base::Histogram::FactoryTimeGet(
+      histogram_name_with_reason, base::TimeDelta::FromMilliseconds(10),
+      base::TimeDelta::FromMinutes(10), 100,
+      base::HistogramBase::kUmaTargetedHistogramFlag);
+  histogram_with_reason->AddTime(step_time);
 }
 
 bool IsRemoraRequisition() {
@@ -792,19 +807,21 @@
   skip_update_enroll_after_eula_ = true;
 }
 
-void WizardController::OnScreenExit(OobeScreenId screen, int exit_code) {
+void WizardController::OnScreenExit(OobeScreenId screen,
+                                    const std::string& exit_reason) {
   DCHECK(current_screen_->screen_id() == screen);
 
-  VLOG(1) << "Wizard screen " << screen << " exited with code: " << exit_code;
+  VLOG(1) << "Wizard screen " << screen
+          << " exited with reason: " << exit_reason;
 
   RecordUMAHistogramForOOBEStepCompletionTime(
-      screen, base::Time::Now() - screen_show_times_[screen]);
+      screen, exit_reason, base::Time::Now() - screen_show_times_[screen]);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 // WizardController, ExitHandlers:
 void WizardController::OnWrongHWIDScreenExit() {
-  OnScreenExit(WrongHWIDScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(WrongHWIDScreenView::kScreenId, kDefaultExitReason);
 
   if (previous_screen_) {
     SetCurrentScreen(previous_screen_);
@@ -814,7 +831,7 @@
 }
 
 void WizardController::OnHidDetectionScreenExit() {
-  OnScreenExit(HIDDetectionView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(HIDDetectionView::kScreenId, kDefaultExitReason);
 
   // Check for tests configuration.
   if (!StartupUtils::IsOobeCompleted())
@@ -822,13 +839,14 @@
 }
 
 void WizardController::OnWelcomeScreenExit() {
-  OnScreenExit(WelcomeView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(WelcomeView::kScreenId, kDefaultExitReason);
 
   ShowNetworkScreen();
 }
 
 void WizardController::OnNetworkScreenExit(NetworkScreen::Result result) {
-  OnScreenExit(NetworkScreenView::kScreenId, static_cast<int>(result));
+  OnScreenExit(NetworkScreenView::kScreenId,
+               NetworkScreen::GetResultString(result));
 
   if (result == NetworkScreen::Result::BACK) {
     if (demo_setup_controller_) {
@@ -895,7 +913,7 @@
 }
 
 void WizardController::OnEulaScreenExit(EulaScreen::Result result) {
-  OnScreenExit(EulaView::kScreenId, static_cast<int>(result));
+  OnScreenExit(EulaView::kScreenId, EulaScreen::GetResultString(result));
 
   switch (result) {
     case EulaScreen::Result::ACCEPTED_WITH_USAGE_STATS_REPORTING:
@@ -934,7 +952,7 @@
 }
 
 void WizardController::OnUpdateScreenExit(UpdateScreen::Result result) {
-  OnScreenExit(UpdateView::kScreenId, static_cast<int>(result));
+  OnScreenExit(UpdateView::kScreenId, UpdateScreen::GetResultString(result));
 
   switch (result) {
     case UpdateScreen::Result::UPDATE_NOT_REQUIRED:
@@ -957,7 +975,7 @@
 }
 
 void WizardController::OnAutoEnrollmentCheckScreenExit() {
-  OnScreenExit(AutoEnrollmentCheckScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(AutoEnrollmentCheckScreenView::kScreenId, kDefaultExitReason);
 
   // Check whether the device is disabled. OnDeviceDisabledChecked() will be
   // invoked when the result of this check is known. Until then, the current
@@ -970,7 +988,8 @@
 }
 
 void WizardController::OnEnrollmentScreenExit(EnrollmentScreen::Result result) {
-  OnScreenExit(EnrollmentScreenView::kScreenId, static_cast<int>(result));
+  OnScreenExit(EnrollmentScreenView::kScreenId,
+               EnrollmentScreen::GetResultString(result));
 
   switch (result) {
     case EnrollmentScreen::Result::COMPLETED:
@@ -1009,26 +1028,27 @@
 }
 
 void WizardController::OnEnableAdbSideloadingScreenExit() {
-  OnScreenExit(EnableAdbSideloadingScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(EnableAdbSideloadingScreenView::kScreenId, kDefaultExitReason);
 
   OnDeviceModificationCanceled();
 }
 
 void WizardController::OnEnableDebuggingScreenExit() {
-  OnScreenExit(EnableDebuggingScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(EnableDebuggingScreenView::kScreenId, kDefaultExitReason);
 
   OnDeviceModificationCanceled();
 }
 
 void WizardController::OnKioskEnableScreenExit() {
-  OnScreenExit(KioskEnableScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(KioskEnableScreenView::kScreenId, kDefaultExitReason);
 
   ShowLoginScreen();
 }
 
 void WizardController::OnKioskAutolaunchScreenExit(
     KioskAutolaunchScreen::Result result) {
-  OnScreenExit(KioskAutolaunchScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(KioskAutolaunchScreenView::kScreenId,
+               KioskAutolaunchScreen::GetResultString(result));
 
   switch (result) {
     case KioskAutolaunchScreen::Result::COMPLETED:
@@ -1043,7 +1063,8 @@
 
 void WizardController::OnDemoPreferencesScreenExit(
     DemoPreferencesScreen::Result result) {
-  OnScreenExit(DemoPreferencesScreenView::kScreenId, static_cast<int>(result));
+  OnScreenExit(DemoPreferencesScreenView::kScreenId,
+               DemoPreferencesScreen::GetResultString(result));
 
   DCHECK(demo_setup_controller_);
 
@@ -1059,7 +1080,8 @@
 }
 
 void WizardController::OnDemoSetupScreenExit(DemoSetupScreen::Result result) {
-  OnScreenExit(DemoSetupScreenView::kScreenId, static_cast<int>(result));
+  OnScreenExit(DemoSetupScreenView::kScreenId,
+               DemoSetupScreen::GetResultString(result));
 
   DCHECK(demo_setup_controller_);
   demo_setup_controller_.reset();
@@ -1077,7 +1099,8 @@
 
 void WizardController::OnTermsOfServiceScreenExit(
     TermsOfServiceScreen::Result result) {
-  OnScreenExit(TermsOfServiceScreenView::kScreenId, static_cast<int>(result));
+  OnScreenExit(TermsOfServiceScreenView::kScreenId,
+               TermsOfServiceScreen::GetResultString(result));
 
   switch (result) {
     case TermsOfServiceScreen::Result::ACCEPTED:
@@ -1096,7 +1119,7 @@
 }
 
 void WizardController::OnSyncConsentScreenExit() {
-  OnScreenExit(SyncConsentScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(SyncConsentScreenView::kScreenId, kDefaultExitReason);
   OnSyncConsentFinished();
 }
 
@@ -1105,20 +1128,20 @@
 }
 
 void WizardController::OnFingerprintSetupScreenExit() {
-  OnScreenExit(FingerprintSetupScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(FingerprintSetupScreenView::kScreenId, kDefaultExitReason);
 
   ShowDiscoverScreen();
 }
 
 void WizardController::OnDiscoverScreenExit() {
-  OnScreenExit(DiscoverScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(DiscoverScreenView::kScreenId, kDefaultExitReason);
   ShowArcTermsOfServiceScreen();
 }
 
 void WizardController::OnArcTermsOfServiceScreenExit(
     ArcTermsOfServiceScreen::Result result) {
   OnScreenExit(ArcTermsOfServiceScreenView::kScreenId,
-               static_cast<int>(result));
+               ArcTermsOfServiceScreen::GetResultString(result));
 
   switch (result) {
     case ArcTermsOfServiceScreen::Result::ACCEPTED:
@@ -1164,7 +1187,8 @@
 
 void WizardController::OnRecommendAppsScreenExit(
     RecommendAppsScreen::Result result) {
-  OnScreenExit(RecommendAppsScreenView::kScreenId, static_cast<int>(result));
+  OnScreenExit(RecommendAppsScreenView::kScreenId,
+               RecommendAppsScreen::GetResultString(result));
 
   switch (result) {
     case RecommendAppsScreen::Result::SELECTED:
@@ -1177,36 +1201,36 @@
 }
 
 void WizardController::OnAppDownloadingScreenExit() {
-  OnScreenExit(AppDownloadingScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(AppDownloadingScreenView::kScreenId, kDefaultExitReason);
 
   ShowAssistantOptInFlowScreen();
 }
 
 void WizardController::OnAssistantOptInFlowScreenExit() {
-  OnScreenExit(AssistantOptInFlowScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(AssistantOptInFlowScreenView::kScreenId, kDefaultExitReason);
 
   ShowMultiDeviceSetupScreen();
 }
 
 void WizardController::OnMultiDeviceSetupScreenExit() {
-  OnScreenExit(MultiDeviceSetupScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(MultiDeviceSetupScreenView::kScreenId, kDefaultExitReason);
 
   ShowGestureNavigationScreen();
 }
 
 void WizardController::OnGestureNavigationScreenExit() {
-  OnScreenExit(GestureNavigationScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(GestureNavigationScreenView::kScreenId, kDefaultExitReason);
 
   ShowMarketingOptInScreen();
 }
 
 void WizardController::OnMarketingOptInScreenExit() {
-  OnScreenExit(MarketingOptInScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(MarketingOptInScreenView::kScreenId, kDefaultExitReason);
   OnOobeFlowFinished();
 }
 
 void WizardController::OnResetScreenExit() {
-  OnScreenExit(ResetView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(ResetView::kScreenId, kResetScreenExitReason);
   OnDeviceModificationCanceled();
 }
 
@@ -1238,14 +1262,15 @@
 }
 
 void WizardController::OnSupervisionTransitionScreenExit() {
-  OnScreenExit(SupervisionTransitionScreenView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(SupervisionTransitionScreenView::kScreenId, kDefaultExitReason);
 
   OnOobeFlowFinished();
 }
 
 void WizardController::OnPackagedLicenseScreenExit(
     PackagedLicenseScreen::Result result) {
-  OnScreenExit(PackagedLicenseView::kScreenId, 0 /* exit_code */);
+  OnScreenExit(PackagedLicenseView::kScreenId,
+               PackagedLicenseScreen::GetResultString(result));
   switch (result) {
     case PackagedLicenseScreen::Result::DONT_ENROLL:
       ShowLoginScreen();
diff --git a/chrome/browser/chromeos/login/wizard_controller.h b/chrome/browser/chromeos/login/wizard_controller.h
index 1be5a63..f960ad6 100644
--- a/chrome/browser/chromeos/login/wizard_controller.h
+++ b/chrome/browser/chromeos/login/wizard_controller.h
@@ -209,8 +209,8 @@
       DeviceSettingsService::OwnershipStatus status);
 
   // Shared actions to be performed on a screen exit.
-  // |exit_code| is the screen specific exit code reported by the screen.
-  void OnScreenExit(OobeScreenId screen, int exit_code);
+  // |exit_reason| is the screen specific exit reason reported by the screen.
+  void OnScreenExit(OobeScreenId screen, const std::string& exit_reason);
 
   // Exit handlers:
   void OnWrongHWIDScreenExit();
diff --git a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
index 7f3ce8a..969cf088 100644
--- a/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.cc
@@ -46,6 +46,7 @@
 #include "components/policy/core/common/cloud/cloud_policy_core.h"
 #include "components/policy/core/common/cloud/cloud_policy_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
 #include "components/policy/core/common/policy_types.h"
 #include "components/policy/core/common/remote_commands/remote_commands_factory.h"
 #include "components/policy/core/common/schema_registry.h"
@@ -326,7 +327,8 @@
 
   // Start remote commands services now that we have setup everything they need.
   core()->StartRemoteCommandsService(
-      std::make_unique<DeviceCommandsFactoryChromeOS>(this));
+      std::make_unique<DeviceCommandsFactoryChromeOS>(this),
+      PolicyInvalidationScope::kDevice);
 
   // Enable device reporting and status monitoring for cloud managed devices. We
   // want to create these objects even if monitoring is currently inactive, in
diff --git a/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc b/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
index 176b25e9..bc204f264 100644
--- a/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/policy/force_maximize_on_first_run_chromeos_browsertest.cc
@@ -4,12 +4,13 @@
 
 #include <string>
 
-#include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "ash/shell.h"
 #include "base/macros.h"
 #include "base/strings/string_number_conversions.h"
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/chromeos/policy/login_policy_test_base.h"
 #include "chrome/browser/chromeos/profiles/profile_helper.h"
 #include "chrome/browser/profiles/profile.h"
@@ -50,11 +51,6 @@
     return CreateBrowser(profile);
   }
 
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    LoginPolicyTestBase::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(ash::switches::kShowWebUiLogin);
-  }
-
  private:
   DISALLOW_COPY_AND_ASSIGN(ForceMaximizeOnFirstRunTest);
 };
@@ -84,7 +80,10 @@
 
 IN_PROC_BROWSER_TEST_F(ForceMaximizeOnFirstRunTest, TwoRuns) {
   SetUpResolution();
-  LogIn(kAccountId, kAccountPassword, kEmptyServices);
+  ash::LoginScreenTestApi::SubmitPassword(AccountId::FromUserEmail(kAccountId),
+                                          kAccountPassword,
+                                          true /* check_if_submittable */);
+  chromeos::test::WaitForPrimaryUserSessionStart();
 
   const Browser* const browser = OpenNewBrowserWindow();
   ASSERT_TRUE(browser);
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc
index 8ffe064..2a3f6dc4 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_remote_powerwash_job_unittest.cc
@@ -18,6 +18,7 @@
 #include "chromeos/dbus/session_manager/fake_session_manager_client.h"
 #include "chromeos/dbus/session_manager/session_manager_client.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
 #include "components/policy/core/common/remote_commands/remote_command_job.h"
 #include "components/policy/core/common/remote_commands/remote_commands_service.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -33,9 +34,10 @@
  public:
   explicit TestingRemoteCommandsService(MockCloudPolicyClient* client)
       : RemoteCommandsService(std::make_unique<DeviceCommandsFactoryChromeOS>(
-                                  nullptr /* policy_manager */),
+                                  /*policy_manager=*/nullptr),
                               client,
-                              nullptr /* store */) {}
+                              /*store=*/nullptr,
+                              PolicyInvalidationScope::kDevice) {}
   // RemoteCommandsService:
   void SetOnCommandAckedCallback(base::OnceClosure callback) override {
     on_command_acked_callback_ = std::move(callback);
diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc
index 3f525b68..2ab1cbf 100644
--- a/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc
+++ b/chrome/browser/chromeos/policy/remote_commands/device_command_wipe_users_job_unittest.cc
@@ -24,6 +24,7 @@
 #include "chrome/test/base/scoped_testing_local_state.h"
 #include "chrome/test/base/testing_browser_process.h"
 #include "components/policy/core/common/cloud/mock_cloud_policy_client.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
 #include "components/policy/core/common/remote_commands/remote_command_job.h"
 #include "components/policy/core/common/remote_commands/remote_commands_service.h"
 #include "components/policy/proto/device_management_backend.pb.h"
@@ -39,10 +40,11 @@
 class TestingRemoteCommandsService : public RemoteCommandsService {
  public:
   explicit TestingRemoteCommandsService(MockCloudPolicyClient* client)
-      : RemoteCommandsService(
-            std::make_unique<DeviceCommandsFactoryChromeOS>(nullptr),
-            client,
-            nullptr /* store */) {}
+      : RemoteCommandsService(std::make_unique<DeviceCommandsFactoryChromeOS>(
+                                  /*policy_manager=*/nullptr),
+                              client,
+                              /*store=*/nullptr,
+                              PolicyInvalidationScope::kDevice) {}
   // RemoteCommandsService:
   void SetOnCommandAckedCallback(base::OnceClosure callback) override {
     on_command_acked_callback_ = std::move(callback);
diff --git a/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc b/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc
index 1073577..78ea3b559 100644
--- a/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc
+++ b/chrome/browser/chromeos/policy/restore_on_startup_browsertest_chromeos.cc
@@ -5,10 +5,11 @@
 #include <memory>
 #include <utility>
 
-#include "ash/public/cpp/ash_switches.h"
+#include "ash/public/cpp/login_screen_test_api.h"
 #include "base/macros.h"
 #include "base/values.h"
 #include "chrome/browser/chrome_notification_types.h"
+#include "chrome/browser/chromeos/login/test/session_manager_state_waiter.h"
 #include "chrome/browser/chromeos/policy/login_policy_test_base.h"
 #include "chrome/browser/prefs/session_startup_pref.h"
 #include "chrome/browser/ui/browser.h"
@@ -36,9 +37,8 @@
 
   // LoginPolicyTestBase:
   void GetMandatoryPoliciesValue(base::DictionaryValue* policy) const override;
-  void SetUpCommandLine(base::CommandLine* command_line) override;
 
-  void LogInAndVerifyStartUpURLs();
+  void VerifyStartUpURLs();
 
  private:
   DISALLOW_COPY_AND_ASSIGN(RestoreOnStartupTestChromeOS);
@@ -57,15 +57,7 @@
   policy->Set(key::kRestoreOnStartupURLs, std::move(urls));
 }
 
-void RestoreOnStartupTestChromeOS::SetUpCommandLine(
-    base::CommandLine* command_line) {
-  LoginPolicyTestBase::SetUpCommandLine(command_line);
-  command_line->AppendSwitch(ash::switches::kShowWebUiLogin);
-}
-
-void RestoreOnStartupTestChromeOS::LogInAndVerifyStartUpURLs() {
-  LogIn(kAccountId, kAccountPassword, kEmptyServices);
-
+void RestoreOnStartupTestChromeOS::VerifyStartUpURLs() {
   const BrowserList* const browser_list = BrowserList::GetInstance();
   ASSERT_EQ(1U, browser_list->size());
   const Browser* const browser = browser_list->get(0);
@@ -80,12 +72,17 @@
 // Verify that the policies are honored on a new user's login.
 IN_PROC_BROWSER_TEST_F(RestoreOnStartupTestChromeOS, PRE_LogInAndVerify) {
   SkipToLoginScreen();
-  LogInAndVerifyStartUpURLs();
+  LogIn(kAccountId, kAccountPassword, kEmptyServices);
+  VerifyStartUpURLs();
 }
 
 // Verify that the policies are honored on an existing user's login.
 IN_PROC_BROWSER_TEST_F(RestoreOnStartupTestChromeOS, LogInAndVerify) {
-  LogInAndVerifyStartUpURLs();
+  ash::LoginScreenTestApi::SubmitPassword(AccountId::FromUserEmail(kAccountId),
+                                          kAccountPassword,
+                                          true /* check_if_submittable */);
+  chromeos::test::WaitForPrimaryUserSessionStart();
+  VerifyStartUpURLs();
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
index dc8efe0..178468d 100644
--- a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
+++ b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.cc
@@ -27,8 +27,23 @@
     base::Value filtered_list,
     PrefValueMap* prefs) {
   DCHECK(filtered_list.is_list());
+  base::Value enums_list(base::Value::Type::LIST);
+  for (const auto& element : filtered_list.GetList()) {
+    enums_list.Append(ConvertToEnum(element.GetString()));
+  }
   prefs->SetValue(policy_prefs::kSystemFeaturesDisableList,
-                  std::move(filtered_list));
+                  std::move(enums_list));
+}
+
+SystemFeature SystemFeaturesDisableListPolicyHandler::ConvertToEnum(
+    const std::string& system_feature) {
+  if (system_feature == "camera")
+    return SystemFeature::CAMERA;
+  if (system_feature == "settings")
+    return SystemFeature::SETTINGS;
+
+  NOTREACHED() << "Unsupported system feature: " << system_feature;
+  return LAST_SYSTEM_FEATURE;
 }
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
index 2971cc80..f6540ffc 100644
--- a/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
+++ b/chrome/browser/chromeos/policy/system_features_disable_list_policy_handler.h
@@ -15,6 +15,15 @@
 
 namespace policy {
 
+// A system feature that can be disabled by SystemFeaturesDisableList policy.
+enum SystemFeature {
+  CAMERA = 0,  // The camera chrome app on Chrome OS.
+  SETTINGS,    // The settings feature on Chrome OS. It includes also Chrome
+               // settings.
+
+  LAST_SYSTEM_FEATURE
+};
+
 class SystemFeaturesDisableListPolicyHandler
     : public policy::ListPolicyHandler {
  public:
@@ -26,6 +35,9 @@
  protected:
   // ListPolicyHandler:
   void ApplyList(base::Value filtered_list, PrefValueMap* prefs) override;
+
+ private:
+  SystemFeature ConvertToEnum(const std::string& system_feature);
 };
 
 }  // namespace policy
diff --git a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
index e2449b3..5ba6633 100644
--- a/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
+++ b/chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.cc
@@ -794,7 +794,8 @@
     return;
 
   core()->StartRemoteCommandsService(
-      std::make_unique<UserCommandsFactoryChromeOS>(profile_));
+      std::make_unique<UserCommandsFactoryChromeOS>(profile_),
+      PolicyInvalidationScope::kUser);
   invalidator_ = std::make_unique<RemoteCommandsInvalidatorImpl>(
       core(), base::DefaultClock::GetInstance(),
       PolicyInvalidationScope::kUser);
diff --git a/chrome/browser/chromeos/preferences_chromeos_browsertest.cc b/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
index f9bd1be..8db2a86 100644
--- a/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
+++ b/chrome/browser/chromeos/preferences_chromeos_browsertest.cc
@@ -6,7 +6,6 @@
 #include <sys/types.h>
 
 #include "ash/public/cpp/ash_pref_names.h"
-#include "ash/public/cpp/ash_switches.h"
 #include "base/command_line.h"
 #include "base/stl_util.h"
 #include "chrome/browser/browser_process.h"
@@ -147,28 +146,13 @@
   DISALLOW_COPY_AND_ASSIGN(PreferencesTest);
 };
 
-class PreferencesTestForceWebUiLogin : public PreferencesTest {
- public:
-  PreferencesTestForceWebUiLogin() = default;
-  ~PreferencesTestForceWebUiLogin() override = default;
-
-  // PreferencesTest:
-  void SetUpCommandLine(base::CommandLine* command_line) override {
-    PreferencesTest::SetUpCommandLine(command_line);
-    command_line->AppendSwitch(ash::switches::kShowWebUiLogin);
-  }
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(PreferencesTestForceWebUiLogin);
-};
-
-IN_PROC_BROWSER_TEST_F(PreferencesTestForceWebUiLogin, PRE_MultiProfiles) {
+IN_PROC_BROWSER_TEST_F(PreferencesTest, PRE_MultiProfiles) {
   RegisterUser(test_users_[0]);
   RegisterUser(test_users_[1]);
   StartupUtils::MarkOobeCompleted();
 }
 
-IN_PROC_BROWSER_TEST_F(PreferencesTestForceWebUiLogin, MultiProfiles) {
+IN_PROC_BROWSER_TEST_F(PreferencesTest, MultiProfiles) {
   user_manager::UserManager* user_manager = user_manager::UserManager::Get();
 
   // Add first user and init its preferences. Check that corresponding
diff --git a/chrome/browser/extensions/api/processes/processes_api.cc b/chrome/browser/extensions/api/processes/processes_api.cc
index e5ef867f..bd04176 100644
--- a/chrome/browser/extensions/api/processes/processes_api.cc
+++ b/chrome/browser/extensions/api/processes/processes_api.cc
@@ -154,25 +154,22 @@
 
   const double cpu_usage = task_manager->GetPlatformIndependentCPUUsage(id);
   if (!std::isnan(cpu_usage))
-    out_process->cpu.reset(new double(cpu_usage));
+    out_process->cpu = std::make_unique<double>(cpu_usage);
 
   const int64_t network_usage = task_manager->GetProcessTotalNetworkUsage(id);
   if (network_usage != -1)
-    out_process->network.reset(new double(static_cast<double>(network_usage)));
+    out_process->network = std::make_unique<double>(network_usage);
 
   int64_t v8_allocated = 0;
   int64_t v8_used = 0;
   if (task_manager->GetV8Memory(id, &v8_allocated, &v8_used)) {
-    out_process->js_memory_allocated.reset(new double(static_cast<double>(
-        v8_allocated)));
-    out_process->js_memory_used.reset(new double(static_cast<double>(v8_used)));
+    out_process->js_memory_allocated = std::make_unique<double>(v8_allocated);
+    out_process->js_memory_used = std::make_unique<double>(v8_used);
   }
 
   const int64_t sqlite_bytes = task_manager->GetSqliteMemoryUsed(id);
-  if (sqlite_bytes != -1) {
-    out_process->sqlite_memory.reset(new double(static_cast<double>(
-        sqlite_bytes)));
-  }
+  if (sqlite_bytes != -1)
+    out_process->sqlite_memory = std::make_unique<double>(sqlite_bytes);
 
   blink::WebCacheResourceTypeStats cache_stats;
   if (task_manager->GetWebCacheStats(id, &cache_stats)) {
diff --git a/chrome/browser/extensions/background_xhr_browsertest.cc b/chrome/browser/extensions/background_xhr_browsertest.cc
index 94786fc..e402bf2 100644
--- a/chrome/browser/extensions/background_xhr_browsertest.cc
+++ b/chrome/browser/extensions/background_xhr_browsertest.cc
@@ -9,7 +9,6 @@
 #include "base/strings/string_number_conversions.h"
 #include "base/strings/string_util.h"
 #include "base/strings/stringprintf.h"
-#include "base/test/scoped_feature_list.h"
 #include "chrome/browser/extensions/extension_apitest.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_with_management_policy_apitest.h"
@@ -35,7 +34,6 @@
 #include "net/ssl/client_cert_store.h"
 #include "net/ssl/ssl_server_config.h"
 #include "net/test/embedded_test_server/embedded_test_server.h"
-#include "services/network/public/cpp/features.h"
 #include "url/gurl.h"
 
 namespace extensions {
@@ -107,18 +105,7 @@
 
 class BackgroundXhrWebstoreTest : public ExtensionApiTestWithManagementPolicy {
  public:
-  BackgroundXhrWebstoreTest() {
-    // TODO(lukasza): https://crbug.com/1061567: Migrate tests related to
-    // cross-origin requests from content scripts into the
-    // CrossOriginReadBlockingExtensionTest suite (which already covers test
-    // matrix of various enabled/disabled features).
-    //
-    // Affected tests:
-    // - BackgroundXhrWebstoreTest.PolicyContentScriptXHR
-    scoped_feature_list_.InitAndDisableFeature(
-        network::features::kCorbAllowlistAlsoAppliesToOorCors);
-  }
-
+  BackgroundXhrWebstoreTest() = default;
   ~BackgroundXhrWebstoreTest() override = default;
 
   void SetUpCommandLine(base::CommandLine* command_line) override {
@@ -182,8 +169,6 @@
   }
 
  private:
-  base::test::ScopedFeatureList scoped_feature_list_;
-
   DISALLOW_COPY_AND_ASSIGN(BackgroundXhrWebstoreTest);
 };
 
@@ -253,67 +238,6 @@
               ::testing::HasSubstr("<head><title>OK</title></head>"));
 }
 
-// Verify that policy blocklists apply to XHRs done from injected scripts.
-IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, PolicyContentScriptXHR) {
-  TestExtensionDir test_dir;
-  test_dir.WriteManifest(R"(
-    {
-      "name": "XHR Content Script Test",
-      "manifest_version": 2,
-      "version": "0.1",
-      "permissions": ["<all_urls>", "tabs"],
-      "background": {"scripts": ["background.js"]}
-    })");
-
-  constexpr char kBackgroundScript[] =
-      R"(function executeFetch(url) {
-           chrome.tabs.executeScript({code: `
-             fetch("${url}")
-             .then(response => response.text())
-             .then(text => domAutomationController.send(text))
-             .catch(err => domAutomationController.send('ERROR: ' + err));
-           `});
-         }
-      )";
-  test_dir.WriteFile(FILE_PATH_LITERAL("background.js"), kBackgroundScript);
-
-  const Extension* extension = LoadExtension(test_dir.UnpackedPath());
-  ASSERT_TRUE(extension);
-
-  // Navigate to a foo.com page.
-  content::WebContents* web_contents =
-      browser()->tab_strip_model()->GetActiveWebContents();
-  GURL page_url(embedded_test_server()->GetURL("foo.com", "/title1.html"));
-  ui_test_utils::NavigateToURL(browser(), page_url);
-  EXPECT_EQ(page_url, web_contents->GetMainFrame()->GetLastCommittedURL());
-
-  // Using "/non-corb.octet-stream" resource (instead of "/simple.html" as in
-  // most other tests here) because XHRs/fetches from content scripts are
-  // subject to CORB (which is already covered by
-  // CrossOriginReadBlockingExtensionTest) and we want to focus the test below
-  // on policy behavior (which should be independent from whether or not CORB
-  // blocks the response).
-  GURL example_url =
-      embedded_test_server()->GetURL("example.com", "/non-corb.octet-stream");
-  GURL public_example_url = embedded_test_server()->GetURL(
-      "public.example.com", "/non-corb.octet-stream");
-
-  // Sanity Check: Should be able to fetch cross origin.
-  EXPECT_EQ("octet-stream-body", ExecuteFetch(extension, example_url));
-  EXPECT_EQ("octet-stream-body", ExecuteFetch(extension, public_example_url));
-
-  {
-    ExtensionManagementPolicyUpdater pref(&policy_provider_);
-    pref.AddPolicyBlockedHost("*", "*://*.example.com");
-    pref.AddPolicyAllowedHost("*", "*://public.example.com");
-  }
-
-  // Policies apply to XHR from a content script.
-  EXPECT_EQ("ERROR: TypeError: Failed to fetch",
-            ExecuteFetch(extension, example_url));
-  EXPECT_EQ("octet-stream-body", ExecuteFetch(extension, public_example_url));
-}
-
 // Make sure the blocklist and allowlist update for both Default and Individual
 // scope policies. Testing with all host permissions granted (<all_urls>).
 IN_PROC_BROWSER_TEST_F(BackgroundXhrWebstoreTest, PolicyUpdateXHR) {
diff --git a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
index d932fbfa..b44b3c3 100644
--- a/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
+++ b/chrome/browser/extensions/corb_and_cors_extension_browsertest.cc
@@ -17,6 +17,7 @@
 #include "chrome/browser/extensions/api/tabs/tabs_api.h"
 #include "chrome/browser/extensions/extension_browsertest.h"
 #include "chrome/browser/extensions/extension_function_test_utils.h"
+#include "chrome/browser/extensions/extension_management_test_util.h"
 #include "chrome/browser/extensions/extension_service.h"
 #include "chrome/browser/extensions/extension_tab_util.h"
 #include "chrome/browser/extensions/tab_helper.h"
@@ -26,6 +27,8 @@
 #include "chrome/browser/ui/browser_navigator_params.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
 #include "chrome/test/base/ui_test_utils.h"
+#include "components/policy/core/browser/browser_policy_connector.h"
+#include "components/policy/core/common/mock_configuration_policy_provider.h"
 #include "content/public/browser/render_frame_host.h"
 #include "content/public/browser/web_contents.h"
 #include "content/public/common/network_service_util.h"
@@ -163,6 +166,14 @@
                                                        disabled_features);
   }
 
+  void SetUpInProcessBrowserTestFixture() override {
+    EXPECT_CALL(policy_provider_, IsInitializationComplete(testing::_))
+        .WillRepeatedly(testing::Return(true));
+    policy_provider_.SetAutoRefresh();
+    policy::BrowserPolicyConnector::SetPolicyProviderForTesting(
+        &policy_provider_);
+  }
+
   bool IsExtensionAllowlisted() {
     return (GetParam() & TestParam::kAllowlisted) != 0;
   }
@@ -326,13 +337,14 @@
   // This verification helper might not work for non-CORB-eligible resources
   // like MIME types not covered by CORB (e.g. application/octet-stream) or
   // same-origin responses.
-  void VerifyFetchFromContentScript(const base::HistogramTester& histograms,
-                                    const std::string& actual_fetch_result,
-                                    const std::string& expected_fetch_result) {
+  void VerifyCorbEligibleFetchFromContentScript(
+      const base::HistogramTester& histograms,
+      const std::string& actual_fetch_result,
+      const std::string& expected_fetch_result) {
     SubprocessMetricsProvider::MergeHistogramDeltasForTesting();
 
-    // VerifyFetchFromContentScript is only called for Content Types covered by
-    // CORB and therefore these requests carry no risk for
+    // VerifyCorbEligibleFetchFromContentScript is only called for Content Types
+    // covered by CORB and therefore these requests carry no risk for
     // CorbAllowlistAlsoAppliesToOorCors - verify that we didn't log the UMA.
     VerifyPassiveUmaForAllowlistForCors(histograms, base::nullopt);
 
@@ -361,6 +373,30 @@
     }
   }
 
+  void VerifyNonCorbElligibleFetchFromContentScript(
+      const base::HistogramTester& histograms,
+      const std::string& actual_fetch_result,
+      const std::string& expected_fetch_result_prefix) {
+    // Verify that CORB sniffing allowed the response.
+    VerifyFetchFromContentScriptWasAllowedByCorb(histograms,
+                                                 true /* expecting_sniffing */);
+
+    if (ShouldAllowlistAlsoApplyToOorCors() &&
+        AreContentScriptFetchesExpectedToBeBlocked()) {
+      // Verify that the response body was blocked by CORS.
+      EXPECT_EQ(kCorsErrorWhenFetching, actual_fetch_result);
+    } else {
+      // Verify that the response body was not blocked by either CORB nor CORS.
+      EXPECT_THAT(actual_fetch_result,
+                  ::testing::StartsWith(expected_fetch_result_prefix));
+    }
+
+    // This is the kind of response (i.e., cross-origin fetch of a non-CORB
+    // type) that could be affected by the planned
+    // CorbAllowlistAlsoAppliesToOorCors feature.
+    VerifyPassiveUmaForAllowlistForCors(histograms, true);
+  }
+
   content::WebContents* active_web_contents() {
     return browser()->tab_strip_model()->GetActiveWebContents();
   }
@@ -487,6 +523,9 @@
         browser()->profile(), extension_->id(), background_script);
   }
 
+ protected:
+  policy::MockConfigurationPolicyProvider policy_provider_;
+
  private:
   void AllowlistExtensionIfNeeded(const Extension& extension) {
     // Sanity check that the field trial param (which has to be registered via
@@ -616,9 +655,10 @@
     std::string fetch_result = PopString(&message_queue);
 
     // Verify whether the fetch worked or not (expectations differ depending on
-    // various factors - see the body of VerifyFetchFromContentScript).
-    VerifyFetchFromContentScript(histograms, fetch_result,
-                                 "nosniff.xml - body\n");
+    // various factors - see the body of
+    // VerifyCorbEligibleFetchFromContentScript).
+    VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                             "nosniff.xml - body\n");
   }
 
   // Test case #2: Declarative script injected after a renderer-initiated
@@ -640,9 +680,10 @@
     std::string fetch_result = PopString(&message_queue);
 
     // Verify whether the fetch worked or not (expectations differ depending on
-    // various factors - see the body of VerifyFetchFromContentScript).
-    VerifyFetchFromContentScript(histograms, fetch_result,
-                                 "nosniff.xml - body\n");
+    // various factors - see the body of
+    // VerifyCorbEligibleFetchFromContentScript).
+    VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                             "nosniff.xml - body\n");
   }
 }
 
@@ -672,9 +713,10 @@
       FetchViaContentScript(cross_site_resource, active_web_contents());
 
   // Verify whether the fetch worked or not (expectations differ depending on
-  // various factors - see the body of VerifyFetchFromContentScript).
-  VerifyFetchFromContentScript(histograms, fetch_result,
-                               "nosniff.xml - body\n");
+  // various factors - see the body of
+  // VerifyCorbEligibleFetchFromContentScript).
+  VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                           "nosniff.xml - body\n");
 }
 
 // Tests that extension permission to bypass CORS is revoked after the extension
@@ -740,8 +782,8 @@
     content::ExecuteScriptAsync(active_web_contents(), kFetchInitiatingScript);
     std::string fetch_result = PopString(&queue);
 
-    VerifyFetchFromContentScript(histograms, fetch_result,
-                                 "nosniff.xml - body\n");
+    VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                             "nosniff.xml - body\n");
   }
 
   // Unload the extension and try fetching again.  The content script should
@@ -772,6 +814,111 @@
   }
 }
 
+// Test that <all_urls> permission does not apply to hosts blocked by policy.
+IN_PROC_BROWSER_TEST_P(CorbAndCorsExtensionBrowserTest,
+                       ContentScriptVsHostBlockedByPolicy_NoSniffXml) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_TRUE(InstallExtensionWithPermissionToAllUrls());
+  {
+    ExtensionManagementPolicyUpdater pref(&policy_provider_);
+    pref.AddPolicyBlockedHost("*", "*://*.example.com");
+    pref.AddPolicyAllowedHost("*", "*://public.example.com");
+  }
+
+  // Navigate to a fetch-initiator.com page.
+  GURL page_url = GetTestPageUrl("fetch-initiator.com");
+  ui_test_utils::NavigateToURL(browser(), page_url);
+  ASSERT_EQ(page_url,
+            active_web_contents()->GetMainFrame()->GetLastCommittedURL());
+  ASSERT_EQ(url::Origin::Create(page_url),
+            active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
+
+  // Test fetch from a host allowed by the policy (and allowed by the extension
+  // permissions).
+  {
+    SCOPED_TRACE(::testing::Message() << "Allowed by policy");
+    base::HistogramTester histograms;
+    GURL cross_site_resource(
+        embedded_test_server()->GetURL("public.example.com", "/nosniff.xml"));
+    std::string fetch_result =
+        FetchViaContentScript(cross_site_resource, active_web_contents());
+
+    // Verify whether the fetch worked or not (expectations differ depending on
+    // various factors - see the body of
+    // VerifyCorbEligibleFetchFromContentScript).
+    VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                             "nosniff.xml - body\n");
+  }
+
+  // Test fetch from a host blocked by the policy (and allowed by the extension
+  // permissions).
+  {
+    SCOPED_TRACE(::testing::Message() << "Blocked by policy");
+    base::HistogramTester histograms;
+    GURL cross_site_resource(
+        embedded_test_server()->GetURL("example.com", "/nosniff.xml"));
+    std::string fetch_result =
+        FetchViaContentScript(cross_site_resource, active_web_contents());
+
+    // Verify that the fetch was blocked by CORS.
+    EXPECT_EQ(kCorsErrorWhenFetching, fetch_result);
+    VerifyFetchFromContentScriptWasBlockedByCorb(histograms);
+  }
+}
+
+// Test that <all_urls> permission does not apply to hosts blocked by policy.
+IN_PROC_BROWSER_TEST_P(CorbAndCorsExtensionBrowserTest,
+                       ContentScriptVsHostBlockedByPolicy_AllowedTextResource) {
+  ASSERT_TRUE(embedded_test_server()->Start());
+  ASSERT_TRUE(InstallExtensionWithPermissionToAllUrls());
+  {
+    ExtensionManagementPolicyUpdater pref(&policy_provider_);
+    pref.AddPolicyBlockedHost("*", "*://*.example.com");
+    pref.AddPolicyAllowedHost("*", "*://public.example.com");
+  }
+
+  // Navigate to a fetch-initiator.com page.
+  GURL page_url = GetTestPageUrl("fetch-initiator.com");
+  ui_test_utils::NavigateToURL(browser(), page_url);
+  ASSERT_EQ(page_url,
+            active_web_contents()->GetMainFrame()->GetLastCommittedURL());
+  ASSERT_EQ(url::Origin::Create(page_url),
+            active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
+
+  // Test fetch from a host allowed by the policy (and allowed by the extension
+  // permissions).
+  {
+    SCOPED_TRACE(::testing::Message() << "Allowed by policy");
+    base::HistogramTester histograms;
+    GURL cross_site_resource(embedded_test_server()->GetURL(
+        "public.example.com", "/save_page/text.txt"));
+    std::string fetch_result =
+        FetchViaContentScript(cross_site_resource, active_web_contents());
+
+    // Verify that the fetch was allowed by CORB.  CORS expectations differ
+    // depending on exact scenario.
+    VerifyNonCorbElligibleFetchFromContentScript(
+        histograms, fetch_result,
+        "text-object.txt: ae52dd09-9746-4b7e-86a6-6ada5e2680c2");
+  }
+
+  // Test fetch from a host blocked by the policy (and allowed by the extension
+  // permissions).
+  {
+    SCOPED_TRACE(::testing::Message() << "Blocked by policy");
+    base::HistogramTester histograms;
+    GURL cross_site_resource(
+        embedded_test_server()->GetURL("example.com", "/save_page/text.txt"));
+    std::string fetch_result =
+        FetchViaContentScript(cross_site_resource, active_web_contents());
+
+    // Verify that the fetch was blocked by CORS.
+    EXPECT_EQ(kCorsErrorWhenFetching, fetch_result);
+    VerifyFetchFromContentScriptWasAllowedByCorb(histograms,
+                                                 true /* expecting_sniffing */);
+  }
+}
+
 IN_PROC_BROWSER_TEST_P(CorbAndCorsExtensionBrowserTest,
                        FromProgrammaticContentScript_PermissionToAllUrls) {
   ASSERT_TRUE(embedded_test_server()->Start());
@@ -794,9 +941,10 @@
       FetchViaContentScript(cross_site_resource, active_web_contents());
 
   // Verify whether the fetch worked or not (expectations differ depending on
-  // various factors - see the body of VerifyFetchFromContentScript).
-  VerifyFetchFromContentScript(histograms, fetch_result,
-                               "nosniff.xml - body\n");
+  // various factors - see the body of
+  // VerifyCorbEligibleFetchFromContentScript).
+  VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                           "nosniff.xml - body\n");
 }
 
 // Coverage of *.subdomain.com extension permissions for CORB-eligible fetches
@@ -827,9 +975,10 @@
         FetchViaContentScript(allowed_url, active_web_contents());
 
     // Verify whether the fetch worked or not (expectations differ depending on
-    // various factors - see the body of VerifyFetchFromContentScript).
-    VerifyFetchFromContentScript(histograms, fetch_result,
-                                 "nosniff.xml - body\n");
+    // various factors - see the body of
+    // VerifyCorbEligibleFetchFromContentScript).
+    VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                             "nosniff.xml - body\n");
   }
 }
 
@@ -862,9 +1011,10 @@
       FetchViaContentScript(redirecting_url, active_web_contents());
 
   // Verify whether the fetch worked or not (expectations differ depending on
-  // various factors - see the body of VerifyFetchFromContentScript).
-  VerifyFetchFromContentScript(histograms, fetch_result,
-                               "nosniff.xml - body\n");
+  // various factors - see the body of
+  // VerifyCorbEligibleFetchFromContentScript).
+  VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                           "nosniff.xml - body\n");
 }
 
 // Test that verifies CORS-allowed fetches work for targets that are not
@@ -986,28 +1136,11 @@
   std::string fetch_result =
       FetchViaContentScript(cross_site_resource, active_web_contents());
 
-  // Verify that CORB sniffing allowed the response.
-  VerifyFetchFromContentScriptWasAllowedByCorb(histograms,
-                                               true /* expecting_sniffing */);
-
-  if (ShouldAllowlistAlsoApplyToOorCors() &&
-      AreContentScriptFetchesExpectedToBeBlocked()) {
-    // Verify that the response body was blocked by CORS.
-    EXPECT_EQ(kCorsErrorWhenFetching, fetch_result);
-  } else {
-    // Verify that the response body was not blocked by either CORB nor CORS.
-    //
-    // StartsWith (rather than equality) is used in the verification step to
-    // account for \n VS \r\n difference on Windows.
-    EXPECT_THAT(fetch_result,
-                ::testing::StartsWith(
-                    "text-object.txt: ae52dd09-9746-4b7e-86a6-6ada5e2680c2"));
-  }
-
-  // This is the kind of response (i.e., cross-origin fetch of a non-CORB type)
-  // that could be affected by the planned CorbAllowlistAlsoAppliesToOorCors
-  // feature.
-  VerifyPassiveUmaForAllowlistForCors(histograms, true);
+  // Verify that the fetch was allowed by CORB.  CORS expectations differ
+  // depending on exact scenario.
+  VerifyNonCorbElligibleFetchFromContentScript(
+      histograms, fetch_result,
+      "text-object.txt: ae52dd09-9746-4b7e-86a6-6ada5e2680c2");
 }
 
 // Coverage of *.subdomain.com extension permissions for non-CORB eligible
@@ -1041,27 +1174,9 @@
         FetchViaContentScript(allowed_url, active_web_contents());
 
     // Verify that CORB sniffing allowed the response.
-    VerifyFetchFromContentScriptWasAllowedByCorb(histograms,
-                                                 true /* expecting_sniffing */);
-
-    if (ShouldAllowlistAlsoApplyToOorCors() &&
-        AreContentScriptFetchesExpectedToBeBlocked()) {
-      // Verify that the response body was blocked by CORS.
-      EXPECT_EQ(kCorsErrorWhenFetching, fetch_result);
-    } else {
-      // Verify that the response body was not blocked by either CORB nor CORS.
-      //
-      // StartsWith (rather than equality) is used in the verification step to
-      // account for \n VS \r\n difference on Windows.
-      EXPECT_THAT(fetch_result,
-                  ::testing::StartsWith(
-                      "text-object.txt: ae52dd09-9746-4b7e-86a6-6ada5e2680c2"));
-    }
-
-    // This is the kind of response (i.e., cross-origin fetch of a non-CORB
-    // type) that could be affected by the planned
-    // CorbAllowlistAlsoAppliesToOorCors feature.
-    VerifyPassiveUmaForAllowlistForCors(histograms, true);
+    VerifyNonCorbElligibleFetchFromContentScript(
+        histograms, fetch_result,
+        "text-object.txt: ae52dd09-9746-4b7e-86a6-6ada5e2680c2");
   }
 }
 
@@ -1090,22 +1205,8 @@
       FetchViaContentScript(cross_site_resource, active_web_contents());
 
   // Verify that CORB sniffing allowed the response.
-  VerifyFetchFromContentScriptWasAllowedByCorb(histograms,
-                                               true /* expecting_sniffing */);
-
-  if (ShouldAllowlistAlsoApplyToOorCors() &&
-      AreContentScriptFetchesExpectedToBeBlocked()) {
-    // Verify that the response body was blocked by CORS.
-    EXPECT_EQ(kCorsErrorWhenFetching, fetch_result);
-  } else {
-    // Verify that the response body was not blocked by either CORB nor CORS.
-    EXPECT_THAT(fetch_result, ::testing::StartsWith("\xEF\xBF\xBDPNG"));
-  }
-
-  // This is the kind of response (i.e., cross-origin fetch that is not blocked
-  // by CORB due to sniffing) that could be affected by the planned
-  // CorbAllowlistAlsoAppliesToOorCors feature.
-  VerifyPassiveUmaForAllowlistForCors(histograms, true);
+  VerifyNonCorbElligibleFetchFromContentScript(histograms, fetch_result,
+                                               "\xEF\xBF\xBDPNG");
 }
 
 // Test that responses are blocked by CORB, but have empty response body are not
@@ -1132,9 +1233,10 @@
       FetchViaContentScript(cross_site_resource, active_web_contents());
 
   // Verify whether the fetch worked or not (expectations differ depending on
-  // various factors - see the body of VerifyFetchFromContentScript).
-  VerifyFetchFromContentScript(histograms, fetch_result,
-                               "" /* expected_response_body */);
+  // various factors - see the body of
+  // VerifyCorbEligibleFetchFromContentScript).
+  VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                           "" /* expected_response_body */);
 }
 
 // Test that LogInitiatorSchemeBypassingDocumentBlocking exits early for
@@ -1457,9 +1559,10 @@
         FetchViaContentScript(cross_site_resource, active_web_contents());
 
     // Verify whether the fetch worked or not (expectations differ depending on
-    // various factors - see the body of VerifyFetchFromContentScript).
-    VerifyFetchFromContentScript(histograms, fetch_result,
-                                 "nosniff.xml - body\n");
+    // various factors - see the body of
+    // VerifyCorbEligibleFetchFromContentScript).
+    VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                             "nosniff.xml - body\n");
   }
   // Using a different image, to bypass renderer-side caching.
   EXPECT_EQ("IMG LOADED",
@@ -1490,9 +1593,10 @@
         FetchViaContentScript(cross_site_resource, active_web_contents());
 
     // Verify whether the fetch worked or not (expectations differ depending on
-    // various factors - see the body of VerifyFetchFromContentScript).
-    VerifyFetchFromContentScript(histograms, fetch_result,
-                                 "nosniff.xml - body\n");
+    // various factors - see the body of
+    // VerifyCorbEligibleFetchFromContentScript).
+    VerifyCorbEligibleFetchFromContentScript(histograms, fetch_result,
+                                             "nosniff.xml - body\n");
   }
   // Using a different image, to bypass renderer-side caching.
   EXPECT_EQ("IMG LOADED",
diff --git a/chrome/browser/memory/memory_kills_monitor.cc b/chrome/browser/memory/memory_kills_monitor.cc
index 645e670..8e31eeb 100644
--- a/chrome/browser/memory/memory_kills_monitor.cc
+++ b/chrome/browser/memory/memory_kills_monitor.cc
@@ -70,10 +70,7 @@
   }
 }
 
-MemoryKillsMonitor::MemoryKillsMonitor()
-    : low_memory_kills_count_(0),
-      last_oom_kill_time_(-1),
-      oom_kills_count_(0) {}
+MemoryKillsMonitor::MemoryKillsMonitor() = default;
 
 MemoryKillsMonitor::~MemoryKillsMonitor() {
   // The instance has to be leaked on shutdown as it is referred to by a
diff --git a/chrome/browser/memory/memory_kills_monitor.h b/chrome/browser/memory/memory_kills_monitor.h
index 20e523d..6906dbb 100644
--- a/chrome/browser/memory/memory_kills_monitor.h
+++ b/chrome/browser/memory/memory_kills_monitor.h
@@ -104,14 +104,14 @@
   base::Time last_low_memory_kill_time_;
   // The number of low memory kills since monitoring is started. Accessed from
   // UI thread only.
-  int low_memory_kills_count_;
+  int low_memory_kills_count_ = 0;
 
   // The last time an OOM kill happens. Accessed from
   // |non_joinable_worker_thread_| only.
-  int64_t last_oom_kill_time_;
+  int64_t last_oom_kill_time_ = -1;
   // The number of OOM kills since monitoring is started. Accessed from
   // |non_joinable_worker_thread_| only.
-  int oom_kills_count_;
+  int oom_kills_count_ = 0;
 
   DISALLOW_COPY_AND_ASSIGN(MemoryKillsMonitor);
 };
diff --git a/chrome/browser/password_manager/password_manager_browsertest.cc b/chrome/browser/password_manager/password_manager_browsertest.cc
index 7fa56a2d..6bbf3c0 100644
--- a/chrome/browser/password_manager/password_manager_browsertest.cc
+++ b/chrome/browser/password_manager/password_manager_browsertest.cc
@@ -24,6 +24,7 @@
 #include "chrome/browser/password_manager/chrome_password_manager_client.h"
 #include "chrome/browser/password_manager/password_manager_test_base.h"
 #include "chrome/browser/password_manager/password_store_factory.h"
+#include "chrome/browser/password_manager/password_store_signin_notifier_impl.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h"
 #include "chrome/browser/ui/autofill/chrome_autofill_client.h"
@@ -439,6 +440,23 @@
   EXPECT_TRUE(prompt_observer->IsSavePromptShownAutomatically());
 }
 
+#if defined(OS_WIN) || defined(OS_MACOSX) || \
+    (defined(OS_LINUX) && !defined(OS_CHROMEOS))
+IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest,
+                       SignOutWithUnsyncedPasswords) {
+  std::vector<autofill::PasswordForm> credentials(1);
+  credentials[0].username_value = base::ASCIIToUTF16("unsynced_login");
+  credentials[0].password_value = base::ASCIIToUTF16("unsynced_password");
+  // TODO(crbug.com/1060132): Stop triggering the notifier directly once the
+  // full flow is available.
+  PasswordStoreSigninNotifierImpl notifier(browser()->profile());
+  notifier.NotifyUISignoutWillDeleteCredentials(credentials);
+  EXPECT_EQ(ManagePasswordsUIController::FromWebContents(WebContents())
+                ->GetUnsyncedCredentials(),
+            credentials);
+}
+#endif
+
 IN_PROC_BROWSER_TEST_F(PasswordManagerBrowserTest, PromptForDynamicForm) {
   // Adding a PSL matching form is a workaround explained later.
   scoped_refptr<password_manager::TestPasswordStore> password_store =
diff --git a/chrome/browser/resources/media/media_data_table.js b/chrome/browser/resources/media/media_data_table.js
index 8efaad5..758ace61 100644
--- a/chrome/browser/resources/media/media_data_table.js
+++ b/chrome/browser/resources/media/media_data_table.js
@@ -80,7 +80,7 @@
             key = k;
           });
 
-          this.delegate_.insertDataField(td, data, key);
+          this.delegate_.insertDataField(td, data, key, dataRow);
           tr.appendChild(td);
         });
       });
@@ -103,8 +103,10 @@
      * @param {Element} td
      * @param {?Object} data
      * @param {string} key
+     * @param {Object} dataRow This is the row itself in case we need extra
+     *   data to render the field.
      */
-    insertDataField(td, data, key) {}
+    insertDataField(td, data, key, dataRow) {}
 
     /**
      * Compares two objects based on |sortKey|.
diff --git a/chrome/browser/resources/media/media_feeds.html b/chrome/browser/resources/media/media_feeds.html
index 3f848c5..1083adcf 100644
--- a/chrome/browser/resources/media/media_feeds.html
+++ b/chrome/browser/resources/media/media_feeds.html
@@ -124,6 +124,8 @@
         </th>
         <th data-key="logos">
           Logos
+        <th data-key="actions">
+          Actions
         </th>
       </tr>
     </thead>
@@ -131,22 +133,71 @@
     </tbody>
   </table>
 
-  <template id="datarow">
-    <tr>
-      <td class="id-cell"></td>
-      <td class="url-cell"></td>
-      <td></td>
-      <td class="last-discovery-time-cell"></td>
-      <td></td>
-      <td></td>
-      <td></td>
-      <td></td>
-      <td></td>
-      <td></td>
-      <td></td>
-      <td></td>
-      <td></td>
-    </tr>
-  </template>
+  <div id="feed-content" style="display:none;">
+    <hr>
+    <h2>Feed Contents: <span id="current-feed"></span></h2>
+    <table id="feed-items-table">
+      <thead>
+        <tr>
+          <th sort-key="type" class="sort-column" sort-reverse>
+            Type
+          </th>
+          <th sort-key="name">
+            Name
+          </th>
+          <th sort-key="author">
+            Author
+          </th>
+          <th sort-key="datePublished">
+            Date Published
+          </th>
+          <th sort-key="isFamilyFriendly">
+            Family Friendly
+          </th>
+          <th sort-key="actionStatus">
+            Action Status
+          </th>
+          <th sort-key="action.url">
+            Action URL
+          </th>
+          <th sort-key="action.startTime">
+            Action Start Time (secs)
+          </th>
+          <th sort-key="interactionCounters">
+            Interaction Counters
+          </th>
+          <th sort-key="contentRatings">
+            Content Ratings
+          </th>
+          <th sort-key="genre">
+            Genre
+          </th>
+          <th sort-key="live">
+            Live Details
+          </th>
+          <th sort-key="tvEpisode">
+            TV Episode
+          </th>
+          <th sort-key="playNextCandidate">
+            Play Next Candidate
+          </th>
+          <th sort-key="identifiers">
+            Identifiers
+          </th>
+          <th sort-key="shownCount">
+            Shown Count
+          </th>
+          <th sort-key="clicked">
+            Clicked
+          </th>
+          <th sort-key="images">
+            Images
+          </th>
+        </tr>
+      </thead>
+      <tbody>
+      </tbody>
+    </table>
+  </div>
 </body>
 </html>
diff --git a/chrome/browser/resources/media/media_feeds.js b/chrome/browser/resources/media/media_feeds.js
index 13830a4..cb517fb 100644
--- a/chrome/browser/resources/media/media_feeds.js
+++ b/chrome/browser/resources/media/media_feeds.js
@@ -11,10 +11,16 @@
   return mediaFeedsPageIsPopulatedResolver.promise;
 }
 
+const mediaFeedItemsPageIsPopulatedResolver = new PromiseResolver();
+function whenFeedTableIsPopulatedForTest() {
+  return mediaFeedItemsPageIsPopulatedResolver.promise;
+}
+
 (function() {
 
 let delegate = null;
 let feedsTable = null;
+let feedItemsTable = null;
 let store = null;
 
 /** @implements {cr.ui.MediaDataTableDelegate} */
@@ -25,8 +31,28 @@
    * @param {Element} td
    * @param {?Object} data
    * @param {string} key
+   * @param {Object} dataRow
    */
-  insertDataField(td, data, key) {
+  insertDataField(td, data, key, dataRow) {
+    if (key == 'actions') {
+      const a = document.createElement('a');
+      a.href = '#feed-content';
+      a.textContent = 'Show Contents';
+      td.appendChild(a);
+
+      a.addEventListener('click', () => {
+        store.getItemsForMediaFeed(dataRow.id).then(response => {
+          feedItemsTable.setData(response.items);
+
+          // Show the feed items section.
+          $('current-feed').textContent = dataRow.url.url;
+          $('feed-content').style.display = 'block';
+
+          mediaFeedItemsPageIsPopulatedResolver.resolve();
+        });
+      });
+    }
+
     if (data === undefined || data === null) {
       return;
     }
@@ -36,11 +62,11 @@
       td.textContent = data.url;
     } else if (
         key === 'lastDiscoveryTime' || key === 'lastFetchTime' ||
-        key === 'cacheExpiryTime') {
+        key === 'cacheExpiryTime' || key === 'datePublished') {
       // Format a mojo time.
       td.textContent =
           convertMojoTimeToJS(/** @type {mojoBase.mojom.Time} */ (data))
-              .toString();
+              .toLocaleString();
     } else if (key === 'userStatus') {
       // Format a FeedUserStatus.
       if (data == mediaFeeds.mojom.FeedUserStatus.kAuto) {
@@ -74,7 +100,7 @@
 
       td.textContent =
           contentTypes.length === 0 ? 'None' : contentTypes.join(',');
-    } else if (key === 'logos') {
+    } else if (key === 'logos' || key === 'images') {
       // Format an array of mojo media images.
       data.forEach((image) => {
         const a = document.createElement('a');
@@ -84,6 +110,129 @@
         td.appendChild(a);
         td.appendChild(document.createElement('br'));
       });
+    } else if (key == 'type') {
+      // Format a MediaFeedItemType.
+      switch (parseInt(data, 10)) {
+        case mediaFeeds.mojom.MediaFeedItemType.kVideo:
+          td.textContent = 'Video';
+          break;
+        case mediaFeeds.mojom.MediaFeedItemType.kTVSeries:
+          td.textContent = 'TV Series';
+          break;
+        case mediaFeeds.mojom.MediaFeedItemType.kMovie:
+          td.textContent = 'Movie';
+          break;
+      }
+    } else if (key == 'isFamilyFriendly' || key == 'clicked') {
+      // Format a boolean.
+      td.textContent = data ? 'Yes' : 'No';
+    } else if (key == 'actionStatus') {
+      // Format a MediaFeedItemActionStatus.
+      switch (parseInt(data, 10)) {
+        case mediaFeeds.mojom.MediaFeedItemActionStatus.kUnknown:
+          td.textContent = 'Unknown';
+          break;
+        case mediaFeeds.mojom.MediaFeedItemActionStatus.kActive:
+          td.textContent = 'Active';
+          break;
+        case mediaFeeds.mojom.MediaFeedItemActionStatus.kPotential:
+          td.textContent = 'Potential';
+          break;
+        case mediaFeeds.mojom.MediaFeedItemActionStatus.kCompleted:
+          td.textContent = 'Completed';
+          break;
+      }
+    } else if (key == 'startTime') {
+      // Format a start time.
+      td.textContent =
+          timeDeltaToSeconds(/** @type {mojoBase.mojom.TimeDelta} */ (data));
+    } else if (key == 'interactionCounters') {
+      // Format interaction counters.
+      const counters = [];
+
+      Object.keys(data).forEach((key) => {
+        let keyString = '';
+
+        switch (parseInt(key, 10)) {
+          case mediaFeeds.mojom.InteractionCounterType.kWatch:
+            keyString = 'Watch';
+            break;
+          case mediaFeeds.mojom.InteractionCounterType.kLike:
+            keyString = 'Like';
+            break;
+          case mediaFeeds.mojom.InteractionCounterType.kDislike:
+            keyString = 'Dislike';
+            break;
+        }
+
+        counters.push(keyString + '=' + data[key]);
+      });
+
+      td.textContent = counters.join(' ');
+    } else if (key == 'contentRatings') {
+      // Format content ratings.
+      const ratings = [];
+
+      data.forEach((rating) => {
+        ratings.push(rating.agency + ' ' + rating.value);
+      });
+
+      td.textContent = ratings.join(', ');
+    } else if (key == 'author') {
+      // Format a mojom author.
+      const a = document.createElement('a');
+      a.href = data.url;
+      a.textContent = data.name;
+      a.target = '_blank';
+      td.appendChild(a);
+    } else if (key == 'name' || key == 'genre') {
+      // Format a mojo string16.
+      td.textContent =
+          decodeString16(/** @type {mojoBase.mojom.String16} */ (data));
+    } else if (key == 'live') {
+      // Format LiveDetails.
+      td.textContent = 'Live';
+
+      if (data.startTime) {
+        td.textContent += ' ' +
+            'StartTime=' +
+            convertMojoTimeToJS(
+                /** @type {mojoBase.mojom.Time} */ (data.startTime))
+                .toLocaleString();
+      }
+
+      if (data.endTime) {
+        td.textContent += ' ' +
+            'EndTime=' +
+            convertMojoTimeToJS(
+                /** @type {mojoBase.mojom.Time} */ (data.endTime))
+                .toLocaleString();
+      }
+    } else if (key == 'tvEpisode') {
+      // Format a TV Episode.
+      td.textContent = data.name + ' EpisodeNumber=' + data.episodeNumber +
+          ' SeasonNumber=' + data.seasonNumber + ' ' +
+          formatIdentifiers(/** @type {Array<mediaFeeds.mojom.Identifier>} */ (
+              data.identifiers));
+    } else if (key == 'playNextCandidate') {
+      // Format a Play Next Candidate.
+      td.textContent = data.name + ' EpisodeNumber=' + data.episodeNumber +
+          ' SeasonNumber=' + data.seasonNumber + ' ' +
+          formatIdentifiers(
+                           /** @type {Array<mediaFeeds.mojom.Identifier>} */ (
+                               data.identifiers)) +
+          ' ActionURL=' + data.action.url.url;
+
+      if (data.action.startTime) {
+        td.textContent +=
+            ' ActionStartTimeSecs=' + timeDeltaToSeconds(data.action.startTime);
+      }
+
+      td.textContent += ' DurationSecs=' + timeDeltaToSeconds(data.duration);
+    } else if (key == 'identifiers') {
+      // Format identifiers.
+      td.textContent = formatIdentifiers(
+          /** @type {Array<mediaFeeds.mojom.Identifier>} */ (data));
     } else {
       td.textContent = data;
     }
@@ -122,6 +271,57 @@
 }
 
 /**
+ * Convert a time delta to seconds.
+ * @param {mojoBase.mojom.TimeDelta} timeDelta
+ * @returns {number}
+ */
+function timeDeltaToSeconds(timeDelta) {
+  return timeDelta.microseconds / 1000 / 1000;
+}
+
+/**
+ * Formats an array of identifiers for display.
+ * @param {Array<mediaFeeds.mojom.Identifier>} mojoIdentifiers
+ * @returns {string}
+ */
+function formatIdentifiers(mojoIdentifiers) {
+  const identifiers = [];
+
+  mojoIdentifiers.forEach((identifier) => {
+    let keyString = '';
+
+    switch (identifier.type) {
+      case mediaFeeds.mojom.Identifier_Type.kTMSRootId:
+        keyString = 'TMSRootId';
+        break;
+      case mediaFeeds.mojom.Identifier_Type.kTMSId:
+        keyString = 'TMSId';
+        break;
+      case mediaFeeds.mojom.Identifier_Type.kPartnerId:
+        keyString = 'PartnerId';
+        break;
+    }
+
+    identifiers.push(keyString + '=' + identifier.value);
+  });
+
+  return identifiers.join(' ');
+}
+
+/**
+ * Parses utf16 coded string.
+ * @param {?mojoBase.mojom.String16} arr
+ * @return {string}
+ */
+function decodeString16(arr) {
+  if (arr == null) {
+    return '';
+  }
+
+  return arr.data.map(ch => String.fromCodePoint(ch)).join('');
+}
+
+/**
  * Converts a mojo time to a JS time.
  * @param {mojoBase.mojom.Time} mojoTime
  * @return {Date}
@@ -160,6 +360,7 @@
 
   delegate = new MediaFeedsTableDelegate();
   feedsTable = new cr.ui.MediaDataTable($('feeds-table'), delegate);
+  feedItemsTable = new cr.ui.MediaDataTable($('feed-items-table'), delegate);
 
   updateFeedsTable();
 
diff --git a/chrome/browser/resources/settings/lazy_load.js b/chrome/browser/resources/settings/lazy_load.js
index 1fca5c5c..1e37f66 100644
--- a/chrome/browser/resources/settings/lazy_load.js
+++ b/chrome/browser/resources/settings/lazy_load.js
@@ -9,10 +9,7 @@
 import './autofill_page/payments_section.m.js';
 import './clear_browsing_data_dialog/clear_browsing_data_dialog.m.js';
 import './search_engines_page/search_engines_page.m.js';
-
-// TODO(https://crbug.com/1026426): Uncomment these imports once the pages have
-// been migrated to Polymer 3.
-// import './privacy_page/cookies_page.m.js';
+import './privacy_page/cookies_page.m.js';
 import './privacy_page/security_keys_subpage.m.js';
 import './privacy_page/security_page.m.js';
 import './site_settings/all_sites.m.js';
@@ -89,7 +86,7 @@
 export {DownloadsBrowserProxyImpl} from './downloads_page/downloads_browser_proxy.m.js';
 export {kMenuCloseDelay} from './languages_page/languages_page.m.js';
 export {LanguagesBrowserProxyImpl} from './languages_page/languages_browser_proxy.m.js';
-export {ChooserType,ContentSetting,ContentSettingsTypes,SiteSettingSource,SITE_EXCEPTION_WILDCARD,SortMethod} from './site_settings/constants.m.js';
+export {ChooserType,ContentSetting,ContentSettingsTypes,CookieControlsMode,SiteSettingSource,SITE_EXCEPTION_WILDCARD,SortMethod} from './site_settings/constants.m.js';
 export {cookieInfo} from './site_settings/cookie_info.m.js';
 export {kControlledByLookup} from './site_settings/site_settings_behavior.m.js';
 export {LocalDataBrowserProxyImpl} from './site_settings/local_data_browser_proxy.m.js';
diff --git a/chrome/browser/resources/settings/privacy_page/BUILD.gn b/chrome/browser/resources/settings/privacy_page/BUILD.gn
index 77b0767..3e6d16b6 100644
--- a/chrome/browser/resources/settings/privacy_page/BUILD.gn
+++ b/chrome/browser/resources/settings/privacy_page/BUILD.gn
@@ -206,6 +206,7 @@
   is_polymer3 = true
   deps = [
     ":collapse_radio_button.m",
+    ":cookies_page.m",
     ":do_not_track_toggle.m",
     ":passwords_leak_detection_toggle.m",
     ":personalization_options.m",
@@ -224,6 +225,21 @@
   ]
 }
 
+js_library("cookies_page.m") {
+  sources = [ "$root_gen_dir/chrome/browser/resources/settings/privacy_page/cookies_page.m.js" ]
+  deps = [
+    "..:metrics_browser_proxy.m",
+    "..:route.m",
+    "..:router.m",
+    "../prefs:prefs_behavior.m",
+    "../site_settings:site_settings_prefs_browser_proxy.m",
+    "//third_party/polymer/v3_0/components-chromium/polymer:polymer_bundled",
+    "//ui/webui/resources/js:assert.m",
+    "//ui/webui/resources/js:web_ui_listener_behavior.m",
+  ]
+  extra_deps = [ ":cookies_page_module" ]
+}
+
 js_library("collapse_radio_button.m") {
   sources = [ "$root_gen_dir/chrome/browser/resources/settings/privacy_page/collapse_radio_button.m.js" ]
   deps = [
@@ -422,6 +438,7 @@
 group("polymer3_elements") {
   public_deps = [
     ":collapse_radio_button_module",
+    ":cookies_page_module",
     ":do_not_track_toggle_module",
     ":modulize",
     ":passwords_leak_detection_toggle_module",
@@ -439,6 +456,21 @@
   ]
 }
 
+polymer_modulizer("cookies_page") {
+  js_file = "cookies_page.js"
+  html_file = "cookies_page.html"
+  html_type = "dom-module"
+  namespace_rewrites = settings_namespace_rewrites
+  auto_imports = settings_auto_imports + [
+                   "chrome/browser/resources/settings/route.html|routes",
+                   "chrome/browser/resources/settings/router.html|Router",
+                   "chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.html|SiteSettingsPrefsBrowserProxy,SiteSettingsPrefsBrowserProxyImpl,ContentSettingProvider,CookieControlsManagedState,DefaultContentSetting",
+                   "chrome/browser/resources/settings/site_settings/constants.html|ContentSettingsTypes,SiteSettingSource,CookieControlsMode,ContentSetting",
+                   "ui/webui/resources/html/assert.html|assert",
+                   "chrome/browser/resources/settings/metrics_browser_proxy.html|PrivacyElementInteractions,MetricsBrowserProxy,MetricsBrowserProxyImpl",
+                 ]
+}
+
 polymer_modulizer("collapse_radio_button") {
   js_file = "collapse_radio_button.js"
   html_file = "collapse_radio_button.html"
diff --git a/chrome/browser/resources/settings/settings.gni b/chrome/browser/resources/settings/settings.gni
index 085d02bf..ca70eee 100644
--- a/chrome/browser/resources/settings/settings.gni
+++ b/chrome/browser/resources/settings/settings.gni
@@ -28,6 +28,7 @@
   "settings.ChromeCleanupRemovalListItem|ChromeCleanupRemovalListItem",
   "settings.ClearBrowsingDataBrowserProxy|ClearBrowsingDataBrowserProxy",
   "settings.ContentSetting|ContentSetting",
+  "settings.CookieControlsMode|CookieControlsMode",
   "settings.CreditCardEntry|CreditCardEntry",
   "settings.BrowserProfile|BrowserProfile",
   "settings.DefaultBrowserBrowserProxy|DefaultBrowserBrowserProxy",
diff --git a/chrome/browser/resources/settings/settings_resources.grd b/chrome/browser/resources/settings/settings_resources.grd
index 746750e..1a8aa9e 100644
--- a/chrome/browser/resources/settings/settings_resources.grd
+++ b/chrome/browser/resources/settings/settings_resources.grd
@@ -863,8 +863,7 @@
                  type="chrome_html" />
       <structure name="IDR_SETTINGS_COOKIES_PAGE_HTML"
                  file="privacy_page/cookies_page.html"
-                 type="chrome_html"
-                 preprocess="true" />
+                 type="chrome_html" />
       <structure name="IDR_SETTINGS_COOKIES_PAGE_JS"
                  file="privacy_page/cookies_page.js"
                  type="chrome_html" />
diff --git a/chrome/browser/resources/settings/settings_resources_v3.grdp b/chrome/browser/resources/settings/settings_resources_v3.grdp
index 42bfe74..10aaf5d 100644
--- a/chrome/browser/resources/settings/settings_resources_v3.grdp
+++ b/chrome/browser/resources/settings/settings_resources_v3.grdp
@@ -455,6 +455,10 @@
            file="${root_gen_dir}/chrome/browser/resources/settings/privacy_page/collapse_radio_button.m.js"
            use_base_dir="false"
            type="BINDATA" />
+  <include name="IDR_SETTINGS_PRIVACY_PAGE_COOKIES_PAGE_M_JS"
+           file="${root_gen_dir}/chrome/browser/resources/settings/privacy_page/cookies_page.m.js"
+           use_base_dir="false"
+           type="BINDATA" />
   <include name="IDR_SETTINGS_PRIVACY_PAGE_DO_NOT_TRACK_TOGGLE_M_JS"
            file="${root_gen_dir}/chrome/browser/resources/settings/privacy_page/do_not_track_toggle.m.js"
            use_base_dir="false"
diff --git a/chrome/browser/resources/settings/site_settings/constants.js b/chrome/browser/resources/settings/site_settings/constants.js
index 3001d74..0bc63a422 100644
--- a/chrome/browser/resources/settings/site_settings/constants.js
+++ b/chrome/browser/resources/settings/site_settings/constants.js
@@ -79,7 +79,7 @@
    * components/content_settings/core/browser/cookie_settings.h
    * @enum {number}
    */
-  const CookieControlsMode = {
+  /* #export */ const CookieControlsMode = {
     DISABLED: 0,
     ENABLED: 1,
     INCOGNITO_ONLY: 2,
diff --git a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
index 1cc8e03..a825c29 100644
--- a/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
+++ b/chrome/browser/resources/settings/site_settings/site_settings_prefs_browser_proxy.js
@@ -57,7 +57,7 @@
  *            blockAll: !ManagedState,
               sessionOnly: !ManagedState}}
  */
-let CookieControlsManagedState;
+/* #export */ let CookieControlsManagedState;
 
 /**
  * Stores origin information. The |hasPermissionSettings| will be set to true
diff --git a/chrome/browser/settings/BUILD.gn b/chrome/browser/settings/BUILD.gn
index 8d8f7d3..49061c59 100644
--- a/chrome/browser/settings/BUILD.gn
+++ b/chrome/browser/settings/BUILD.gn
@@ -5,73 +5,9 @@
 import("//build/config/android/rules.gni")
 
 android_library("java") {
-  sources = [
-    "android/java/src/org/chromium/chrome/browser/settings/ManagedPreferenceDelegate.java",
-    "android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtils.java",
-    "android/java/src/org/chromium/chrome/browser/settings/SearchUtils.java",
-    "android/java/src/org/chromium/chrome/browser/settings/SettingsUtils.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/ButtonPreference.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBaseCheckBoxPreference.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBasePreference.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreference.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/ChromeSwitchPreference.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/ExpandablePreferenceGroup.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java",
-    "android/widget/java/src/org/chromium/chrome/browser/settings/TextMessagePreference.java",
-  ]
-  deps = [
-    ":java_resources",
-    "//base:base_java",
-    "//third_party/android_deps:android_support_v7_appcompat_java",
-    "//third_party/android_deps:androidx_annotation_annotation_java",
-    "//third_party/android_deps:androidx_preference_preference_java",
-
-    # TODO(crbug.com/1017190): Remove the following deps once we stop linting individual targets.
-    "//components/browser_ui/styles/android:java_resources",
-    "//third_party/android_deps:com_google_android_material_material_java",
-    "//ui/android:ui_java",
-  ]
-}
-
-android_library("chrome_managed_preference_delegate_java") {
   sources = [ "android/java/src/org/chromium/chrome/browser/settings/ChromeManagedPreferenceDelegate.java" ]
   deps = [
-    ":java",
     "//chrome/browser/preferences:java",
+    "//components/browser_ui/settings/android:java",
   ]
 }
-
-android_resources("java_resources") {
-  deps = [
-    "//chrome/browser/ui/android/strings:ui_strings_grd",
-    "//components/browser_ui/styles/android:java_resources",
-    "//third_party/android_deps:androidx_preference_preference_java",
-    "//third_party/android_deps:com_google_android_material_material_java",
-    "//ui/android:ui_java_resources",
-  ]
-  sources = [
-    "android/java/res/drawable-hdpi/controlled_setting_mandatory.png",
-    "android/java/res/drawable-hdpi/ic_account_child_grey600_36dp.png",
-    "android/java/res/drawable-mdpi/controlled_setting_mandatory.png",
-    "android/java/res/drawable-mdpi/ic_account_child_grey600_36dp.png",
-    "android/java/res/drawable-xhdpi/controlled_setting_mandatory.png",
-    "android/java/res/drawable-xhdpi/ic_account_child_grey600_36dp.png",
-    "android/java/res/drawable-xxhdpi/controlled_setting_mandatory.png",
-    "android/java/res/drawable-xxhdpi/ic_account_child_grey600_36dp.png",
-    "android/java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png",
-    "android/java/res/layout/button_preference_button.xml",
-    "android/java/res/layout/button_preference_layout.xml",
-    "android/java/res/layout/checkable_image_view_widget.xml",
-    "android/java/res/layout/preference_chrome_image_view.xml",
-    "android/java/res/layout/preference_compat.xml",
-    "android/java/res/layout/preference_spinner.xml",
-    "android/java/res/layout/preference_spinner_single_line.xml",
-    "android/java/res/layout/preference_spinner_single_line_item.xml",
-    "android/java/res/values-sw720dp-v17/styles.xml",
-    "android/java/res/values-sw720dp-v21/styles.xml",
-    "android/java/res/values-v21/styles.xml",
-    "android/java/res/values/attrs.xml",
-    "android/java/res/values/styles.xml",
-  ]
-  custom_package = "org.chromium.chrome.browser.settings"
-}
diff --git a/chrome/browser/settings/README.md b/chrome/browser/settings/README.md
deleted file mode 100644
index add2864a..0000000
--- a/chrome/browser/settings/README.md
+++ /dev/null
@@ -1,22 +0,0 @@
-# Chrome for Android Settings
-
-## Getting Started
-
-The Android developer [Settings
-guide](https://developer.android.com/guide/topics/ui/settings) is the best place
-to start before contributing to Chrome for Android's settings.
-
-## Helper Classes
-
-Many common utility functions that are useful for developing settings screens in
-Chrome for Android can be found in `//chrome/browser/settings/android`.
-
-## Widgets
-
-The `widget` subdirectory contains a number of extensions of AndroidX
-[Preference](https://developer.android.com/reference/androidx/preference/Preference)
-classes that provide Chrome-specific behavior (like Managed preferences) or
-common Chrome UI components (like buttons).
-
-The base Preference classes included in the AndroidX Preference library can also
-be used directly in Chrome for Android Settings screens.
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeManagedPreferenceDelegate.java b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeManagedPreferenceDelegate.java
index f5da4e8..40ec801 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeManagedPreferenceDelegate.java
+++ b/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ChromeManagedPreferenceDelegate.java
@@ -8,6 +8,7 @@
 
 import org.chromium.chrome.browser.preferences.Pref;
 import org.chromium.chrome.browser.preferences.PrefServiceBridge;
+import org.chromium.components.browser_ui.settings.ManagedPreferenceDelegate;
 
 /**
  * A ManagedPreferenceDelegate with Chrome-specific default behavior.
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index 4568bb5..1e9de5eaa 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -627,6 +627,9 @@
     if (enable_feed_in_chrome) {
       deps += [ "//chrome/browser/ui/webui/feed_internals:mojo_bindings" ]
     }
+  } else {
+    # !is_android
+    deps += [ "//components/autofill/content/browser/webauthn" ]
   }
 
   if (!is_fuchsia) {
@@ -2765,6 +2768,8 @@
       # This test header is included because it contains forward declarations
       # needed for "friend" statements for use in tests.
       "translate/translate_bubble_test_utils.h",
+      "views/accessibility/caption_bubble.cc",
+      "views/accessibility/caption_bubble.h",
       "views/accessibility/invert_bubble_view.cc",
       "views/accessibility/invert_bubble_view.h",
       "views/accessibility/non_accessible_image_view.cc",
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings.grd b/chrome/browser/ui/android/strings/android_chrome_strings.grd
index 348d9c06..a16b924f 100644
--- a/chrome/browser/ui/android/strings/android_chrome_strings.grd
+++ b/chrome/browser/ui/android/strings/android_chrome_strings.grd
@@ -205,18 +205,6 @@
       <message name="IDS_MENU_HELP" desc="Menu item for opening the help page. [CHAR-LIMIT=27]">
         Help &amp; feedback
       </message>
-      <message name="IDS_MANAGED_BY_YOUR_ORGANIZATION" desc="Popup message when the user clicks a UI element that has been disabled by enterprise policy.">
-        This setting is enforced by your administrator.
-      </message>
-      <message name="IDS_MANAGED_BY_YOUR_PARENTS" desc="Popup message when the user clicks a UI element that has been disabled by their parents.">
-        Managed by your parents
-      </message>
-      <message name="IDS_MANAGED_BY_YOUR_PARENT" desc="Popup message when the user clicks a UI element that has been disabled by their parent.">
-        Managed by your parent
-      </message>
-      <message name="IDS_MANAGED_SETTINGS_CANNOT_BE_RESET" desc="Popup message when the user clicks a UI element that resets a list of settings that include managed settings.">
-        Managed settings cannot be reset
-      </message>
       <message name="IDS_PREFS_SECTION_BASICS" desc='Title of "Basics" section of preferences. [CHAR-LIMIT=32]'>
         Basics
       </message>
@@ -981,12 +969,6 @@
       <message name="IDS_WEBSITE_SETTINGS_EXCEPTIONS_GROUP_HEADING" desc="The heading for a list of websites that have been granted an exception to access a particular permission, e.g. to access the user's location.">
         Exceptions
       </message>
-      <message name="IDS_ACCESSIBILITY_EXPANDED_GROUP" desc="The accessibility text to read when the selected widget is expanded.">
-        Expanded - click to collapse.
-      </message>
-      <message name="IDS_ACCESSIBILITY_COLLAPSED_GROUP" desc="The accessibility text to read when the selected widget is collapsed.">
-        Collapsed - click to expand.
-      </message>
       <message name="IDS_WEBSITE_SETTINGS_CATEGORY_ALLOWED_RECOMMENDED" desc="Summary text explaining that a permission is allowed, which is the recommended setting.">
         Allowed (recommended)
       </message>
@@ -1555,8 +1537,17 @@
       <message name="IDS_OPEN_SOURCE_LICENSE_URL" desc="URL for open source license" translateable="false">
         chrome://credits
       </message>
-      <message name="IDS_TERMS_OF_SERVICE_TITLE" desc="Title for terms of service">
-        Chrome Terms of Service
+      <message name="IDS_GOOGLE_TERMS_OF_SERVICE_TITLE" desc="Title for Google terms of service">
+        Google Terms of Service
+      </message>
+      <message name="IDS_GOOGLE_TERMS_OF_SERVICE_URL" desc="URL for Google terms of service" translateable="false">
+        https://policies.google.com/terms
+      </message>
+      <message name="IDS_CHROME_ADDITIONAL_TERMS_OF_SERVICE_TITLE" desc="Title for Chrome and Chrome OS addtional terms of service">
+        Chrome &amp; Chrome OS Additional Terms of Service
+      </message>
+      <message name="IDS_CHROME_ADDITIONAL_TERMS_OF_SERVICE_URL" desc="URL for Chrome and Chrome OS additional terms of service" translateable="false">
+        https://www.google.com/chrome/terms/
       </message>
       <message name="IDS_PRIVACY_NOTICE_TITLE" desc="Title for the Chrome privacy notice">
         Chrome Privacy Notice
@@ -1564,9 +1555,6 @@
       <message name="IDS_CHROME_PRIVACY_NOTICE_URL" desc="URL for the Chrome privacy notice" translateable="false">
         https://www.google.com/intl/$LOCALE/chrome/browser/privacy/
       </message>
-      <message name="IDS_CHROME_TERMS_OF_SERVICE_URL" desc="URL for Google Chrome Terms of Service" translateable="false">
-        https://www.google.com/intl/$LOCALE/chrome/browser/privacy/eula_text.html
-      </message>
       <message name="IDS_FAMILY_LINK_PRIVACY_POLICY_URL" desc="URL for the Family Link privacy policy" translateable="false">
         https://families.google.com/intl/$LOCALE/familylink/privacy/child-policy/
       </message>
@@ -2563,17 +2551,17 @@
       <message name="IDS_FRE_ACTIVITY_LABEL" desc="Label for first run dialog in Android Recents.">
         Chrome First Run Experience
       </message>
-      <message name="IDS_FRE_TOS_AND_PRIVACY" desc="Message explaining that use of Chrome is governed by Chrome's terms of service and privacy notice.">
-        By using this application, you agree to Chrome’s <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph> and <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Privacy Notice<ph name="END_LINK2">&lt;/LINK2&gt;</ph>.
+      <message name="IDS_FRE_TOS" desc="Message explaining that use of Chrome is governed by terms of service.">
+        By using Chrome, you agree to the <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Google Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph>, and the <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Google Chrome and Chrome OS Additional Terms of Service<ph name="END_LINK2">&lt;/LINK2&gt;</ph>.
       </message>
-      <message name="IDS_FRE_TOS_AND_PRIVACY_CHILD_ACCOUNT" desc="Message explaining that use of Chrome is governed by Chrome's terms of service and privacy notice, and Family Link's privacy notice for children.">
-        By using this application, you agree to Chrome’s <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph> and <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Privacy Notice<ph name="END_LINK2">&lt;/LINK2&gt;</ph>, and the <ph name="BEGIN_LINK3">&lt;LINK3&gt;</ph>Privacy Notice for Google Accounts Managed with Family Link<ph name="END_LINK3">&lt;/LINK3&gt;</ph>.
+      <message name="IDS_FRE_TOS_AND_PRIVACY_CHILD_ACCOUNT" desc="Message explaining that use of Chrome is governed by Chrome's terms of service, and Family Link's privacy notice for children.">
+        By using Chrome, you agree to the <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Google Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph>, and the <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Google Chrome and Chrome OS Additional Terms of Service<ph name="END_LINK2">&lt;/LINK2&gt;</ph>. The <ph name="BEGIN_LINK3">&lt;LINK3&gt;</ph>Privacy Notice for Google Accounts Managed with Family Link<ph name="END_LINK3">&lt;/LINK3&gt;</ph> also applies.
       </message>
-      <message name="IDS_LIGHTWEIGHT_FRE_ASSOCIATED_APP_TOS_AND_PRIVACY" desc="Message explaining that use of Chrome is governed by Chrome's terms of service and privacy notice.">
-        <ph name="APP_NAME">%1$s<ex>Google Maps</ex></ph> will open in Chrome. By continuing, you agree to Chrome’s <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph> and <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Privacy Notice<ph name="END_LINK2">&lt;/LINK2&gt;</ph>.
+      <message name="IDS_LIGHTWEIGHT_FRE_ASSOCIATED_APP_TOS" desc="Message explaining that use of Chrome is governed by Chrome's terms of service.">
+        <ph name="APP_NAME">%1$s<ex>Google Maps</ex></ph> will open in Chrome. By continuing, you agree to the <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Google Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph>, and the <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Google Chrome and Chrome OS Additional Terms of Service<ph name="END_LINK2">&lt;/LINK2&gt;</ph>.
       </message>
-      <message name="IDS_LIGHTWEIGHT_FRE_ASSOCIATED_APP_TOS_AND_PRIVACY_CHILD_ACCOUNT" desc="Message explaining that use of Chrome is governed by Chrome's terms of service and privacy notice, and Family Link's privacy notice for children.">
-        <ph name="APP_NAME">%1$s<ex>Google Maps</ex></ph> will open in Chrome. By continuing, you agree to Chrome's <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph> and <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Privacy Notice<ph name="END_LINK2">&lt;/LINK2&gt;</ph>, and the <ph name="BEGIN_LINK3">&lt;LINK3&gt;</ph>Privacy Notice for Google Accounts Managed with Family Link<ph name="END_LINK3">&lt;/LINK3&gt;</ph>.
+      <message name="IDS_LIGHTWEIGHT_FRE_ASSOCIATED_APP_TOS_AND_PRIVACY_CHILD_ACCOUNT" desc="Message explaining that use of Chrome is governed by Chrome's terms of service, and Family Link's privacy notice for children.">
+        <ph name="APP_NAME">%1$s<ex>Google Maps</ex></ph> will open in Chrome. By continuing, you agree to the <ph name="BEGIN_LINK1">&lt;LINK1&gt;</ph>Google Terms of Service<ph name="END_LINK1">&lt;/LINK1&gt;</ph>, and the <ph name="BEGIN_LINK2">&lt;LINK2&gt;</ph>Google Chrome and Chrome OS Additional Terms of Service<ph name="END_LINK2">&lt;/LINK2&gt;</ph>. The <ph name="BEGIN_LINK3">&lt;LINK3&gt;</ph>Privacy Notice for Google Accounts Managed with Family Link<ph name="END_LINK3">&lt;/LINK3&gt;</ph> also applies.
       </message>
       <message name="IDS_FRE_SEND_REPORT_CHECK" desc="Text for asking the user to allow sending stats and crash reports">
         Help make Chrome better by sending usage statistics and crash reports to Google.
diff --git a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
index 87eb84f..3c65c542 100644
--- a/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
+++ b/chrome/browser/ui/ash/launcher/chrome_launcher_controller_unittest.cc
@@ -850,7 +850,7 @@
   views::Widget* CreateArcWindow(const std::string& window_app_id) {
     views::Widget::InitParams params(views::Widget::InitParams::TYPE_WINDOW);
     params.bounds = gfx::Rect(5, 5, 20, 20);
-    params.context = ash_test_helper()->GetContext();
+    params.context = GetContext();
     views::Widget* widget = new views::Widget();
     widget->Init(std::move(params));
     // Set ARC id before showing the window to be recognized in
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc
index 8e6a6ef..44f9a00 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.cc
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -39,6 +39,7 @@
 #include "components/autofill/content/browser/autofill_log_router_factory.h"
 #include "components/autofill/content/browser/content_autofill_driver.h"
 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
+#include "components/autofill/content/browser/webauthn/internal_authenticator_impl.h"
 #include "components/autofill/core/browser/form_data_importer.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "components/autofill/core/browser/ui/payments/card_unmask_prompt_view.h"
@@ -226,6 +227,16 @@
 }
 #endif
 
+std::unique_ptr<InternalAuthenticator>
+ChromeAutofillClient::CreateCreditCardInternalAuthenticator(
+    content::RenderFrameHost* rfh) {
+#if defined(OS_ANDROID)
+  return nullptr;
+#else
+  return std::make_unique<content::InternalAuthenticatorImpl>(rfh);
+#endif
+}
+
 void ChromeAutofillClient::ShowAutofillSettings(
     bool show_credit_card_settings) {
 #if defined(OS_ANDROID)
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h
index e5c32bedd..c5a66ae 100644
--- a/chrome/browser/ui/autofill/chrome_autofill_client.h
+++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -74,6 +74,8 @@
   std::vector<std::string> GetMerchantWhitelistForVirtualCards() override;
   std::vector<std::string> GetBinRangeWhitelistForVirtualCards() override;
 #endif
+  std::unique_ptr<InternalAuthenticator> CreateCreditCardInternalAuthenticator(
+      content::RenderFrameHost* rfh) override;
 
   void ShowAutofillSettings(bool show_credit_card_settings) override;
   void ShowUnmaskPrompt(const CreditCard& card,
diff --git a/chrome/browser/ui/passwords/manage_passwords_state.cc b/chrome/browser/ui/passwords/manage_passwords_state.cc
index 79a5daa..263ef48 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_state.cc
@@ -211,6 +211,13 @@
     client_->UpdateFormManagers();
 }
 
+void ManagePasswordsState::ProcessUnsyncedCredentialsWillBeDeleted(
+    const std::vector<autofill::PasswordForm>& unsynced_credentials) {
+  unsynced_credentials_ = unsynced_credentials;
+  // TODO(crbug.com/1060132): Set the state to trigger the corresponding
+  // recovery bubble.
+}
+
 void ManagePasswordsState::ChooseCredential(const PasswordForm* form) {
   DCHECK_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE, state());
   DCHECK(!credentials_callback().is_null());
diff --git a/chrome/browser/ui/passwords/manage_passwords_state.h b/chrome/browser/ui/passwords/manage_passwords_state.h
index 41943afe..f21a35a 100644
--- a/chrome/browser/ui/passwords/manage_passwords_state.h
+++ b/chrome/browser/ui/passwords/manage_passwords_state.h
@@ -85,12 +85,18 @@
   void ProcessLoginsChanged(
       const password_manager::PasswordStoreChangeList& changes);
 
+  void ProcessUnsyncedCredentialsWillBeDeleted(
+      const std::vector<autofill::PasswordForm>& unsynced_credentials);
+
   // Called when the user chooses a credential. |form| is passed to the
   // credentials callback. Method should be called in the
   // CREDENTIAL_REQUEST_STATE state.
   void ChooseCredential(const autofill::PasswordForm* form);
 
   password_manager::ui::State state() const { return state_; }
+  const std::vector<autofill::PasswordForm>& unsynced_credentials() const {
+    return unsynced_credentials_;
+  }
   const GURL& origin() const { return origin_; }
   password_manager::PasswordFormManagerForUI* form_manager() const {
     return form_manager_.get();
@@ -127,6 +133,9 @@
   // Contains all the current forms.
   std::vector<std::unique_ptr<autofill::PasswordForm>> local_credentials_forms_;
 
+  // Contains any non synced credentials.
+  std::vector<autofill::PasswordForm> unsynced_credentials_;
+
   // A callback to be invoked when user selects a credential.
   CredentialsCallback credentials_callback_;
 
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
index 16e22b22..e3decb0 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.cc
@@ -32,6 +32,7 @@
 #include "chrome/browser/ui/passwords/manage_passwords_icon_view.h"
 #include "chrome/browser/ui/passwords/manage_passwords_view_utils.h"
 #include "chrome/browser/ui/passwords/password_dialog_prompts.h"
+#include "chrome/browser/ui/simple_message_box.h"
 #include "chrome/browser/ui/tab_dialogs.h"
 #include "chrome/common/url_constants.h"
 #include "chrome/grit/generated_resources.h"
@@ -283,7 +284,8 @@
 
 void ManagePasswordsUIController::NotifyUnsyncedCredentialsWillBeDeleted(
     const std::vector<autofill::PasswordForm>& unsynced_credentials) {
-  NOTIMPLEMENTED();
+  passwords_data_.ProcessUnsyncedCredentialsWillBeDeleted(unsynced_credentials);
+  // TODO(crbug.com/1060132): Update the bubble after the state is set.
 }
 
 void ManagePasswordsUIController::OnLoginsChanged(
@@ -373,6 +375,11 @@
              : password_manager::metrics_util::CredentialSourceType::kUnknown;
 }
 
+const std::vector<autofill::PasswordForm>&
+ManagePasswordsUIController::GetUnsyncedCredentials() const {
+  return passwords_data_.unsynced_credentials();
+}
+
 const std::vector<std::unique_ptr<autofill::PasswordForm>>&
 ManagePasswordsUIController::GetCurrentForms() const {
   return passwords_data_.GetCurrentForms();
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
index 9d16220..15e51d4 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller.h
@@ -120,6 +120,8 @@
       override;
   password_manager::ui::State GetState() const override;
   const autofill::PasswordForm& GetPendingPassword() const override;
+  const std::vector<autofill::PasswordForm>& GetUnsyncedCredentials()
+      const override;
   password_manager::metrics_util::CredentialSourceType GetCredentialSource()
       const override;
   const std::vector<std::unique_ptr<autofill::PasswordForm>>& GetCurrentForms()
diff --git a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
index e6d4528..f909130a 100644
--- a/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
+++ b/chrome/browser/ui/passwords/manage_passwords_ui_controller_unittest.cc
@@ -1324,3 +1324,15 @@
   ExpectIconAndControllerStateIs(
       password_manager::ui::PENDING_PASSWORD_UPDATE_STATE);
 }
+
+TEST_F(ManagePasswordsUIControllerTest,
+       NotifyUnsyncedCredentialsWillBeDeleted) {
+  // TODO(crbug.com/1060132): Add expectations on the state enum and on the
+  // bubble behavior when those are being updated.
+  std::vector<autofill::PasswordForm> credentials(1);
+  credentials[0].username_value = base::ASCIIToUTF16("unsynced_login");
+  credentials[0].password_value = base::ASCIIToUTF16("unsynced_password");
+  controller()->NotifyUnsyncedCredentialsWillBeDeleted(credentials);
+
+  EXPECT_EQ(controller()->GetUnsyncedCredentials(), credentials);
+}
diff --git a/chrome/browser/ui/passwords/passwords_model_delegate.h b/chrome/browser/ui/passwords/passwords_model_delegate.h
index 065a9485..9ef9550c 100644
--- a/chrome/browser/ui/passwords/passwords_model_delegate.h
+++ b/chrome/browser/ui/passwords/passwords_model_delegate.h
@@ -60,6 +60,10 @@
   // the returned credential in AUTO_SIGNIN_STATE.
   virtual const autofill::PasswordForm& GetPendingPassword() const = 0;
 
+  // Returns unsynced credentials being deleted upon signout.
+  virtual const std::vector<autofill::PasswordForm>& GetUnsyncedCredentials()
+      const = 0;
+
   // Returns the source of the credential to be saved.
   virtual password_manager::metrics_util::CredentialSourceType
   GetCredentialSource() const = 0;
diff --git a/chrome/browser/ui/passwords/passwords_model_delegate_mock.h b/chrome/browser/ui/passwords/passwords_model_delegate_mock.h
index 2f3f032..535293d 100644
--- a/chrome/browser/ui/passwords/passwords_model_delegate_mock.h
+++ b/chrome/browser/ui/passwords/passwords_model_delegate_mock.h
@@ -28,6 +28,8 @@
   MOCK_CONST_METHOD0(GetOrigin, const GURL&());
   MOCK_CONST_METHOD0(GetState, password_manager::ui::State());
   MOCK_CONST_METHOD0(GetPendingPassword, const autofill::PasswordForm&());
+  MOCK_CONST_METHOD0(GetUnsyncedCredentials,
+                     const std::vector<autofill::PasswordForm>&());
   MOCK_CONST_METHOD0(GetCredentialSource,
                      password_manager::metrics_util::CredentialSourceType());
   MOCK_CONST_METHOD0(
diff --git a/chrome/browser/accessibility/caption_bubble.cc b/chrome/browser/ui/views/accessibility/caption_bubble.cc
similarity index 84%
rename from chrome/browser/accessibility/caption_bubble.cc
rename to chrome/browser/ui/views/accessibility/caption_bubble.cc
index a9e13b0..221428b 100644
--- a/chrome/browser/accessibility/caption_bubble.cc
+++ b/chrome/browser/ui/views/accessibility/caption_bubble.cc
@@ -2,19 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/accessibility/caption_bubble.h"
+#include "chrome/browser/ui/views/accessibility/caption_bubble.h"
 
 #include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "base/strings/utf_string_conversions.h"
 #include "ui/base/hit_test.h"
-#include "ui/gfx/geometry/insets.h"
 #include "ui/views/bubble/bubble_dialog_delegate_view.h"
 #include "ui/views/bubble/bubble_frame_view.h"
-#include "ui/views/controls/label.h"
 #include "ui/views/layout/box_layout.h"
 
 // CaptionBubble implementation of BubbleFrameView.
@@ -68,20 +65,19 @@
   set_color(SK_ColorGRAY);
   set_close_on_deactivate(false);
 
-  label_ = new views::Label();
-  label_->SetMultiLine(true);
-  label_->SetMaxLines(2);
+  label_.SetMultiLine(true);
+  label_.SetMaxLines(2);
   int max_width = GetAnchorView()->width() * 0.8;
-  label_->SetMaximumWidth(max_width);
-  label_->SetEnabledColor(SK_ColorWHITE);
-  label_->SetBackgroundColor(SK_ColorTRANSPARENT);
-  label_->SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
-  label_->SetLineHeight(18);
+  label_.SetMaximumWidth(max_width);
+  label_.SetEnabledColor(SK_ColorWHITE);
+  label_.SetBackgroundColor(SK_ColorTRANSPARENT);
+  label_.SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT);
+  label_.SetLineHeight(18);
 
   std::vector<std::string> font_names = {"Arial", "Helvetica"};
   gfx::FontList* font_list = new gfx::FontList(
       font_names, gfx::Font::FontStyle::NORMAL, 14, gfx::Font::Weight::NORMAL);
-  label_->SetFontList(*font_list);
+  label_.SetFontList(*font_list);
 
   // Add some dummy text while this is in development.
   std::string text =
@@ -90,9 +86,9 @@
       "life, which have received widespread media coverage. At age 14, Swift "
       "became the youngest artist signed by the Sony/ATV Music publishing "
       "house and, at age 15, she signed her first record deal.";
-  label_->SetText(base::ASCIIToUTF16(text));
+  label_.SetText(base::ASCIIToUTF16(text));
 
-  AddChildView(label_);
+  AddChildView(&label_);
 }
 
 bool CaptionBubble::ShouldShowCloseButton() const {
@@ -111,7 +107,7 @@
 }
 
 void CaptionBubble::SetText(const std::string& text) {
-  label_->SetText(base::ASCIIToUTF16(text));
+  label_.SetText(base::ASCIIToUTF16(text));
 }
 
 }  // namespace captions
diff --git a/chrome/browser/accessibility/caption_bubble.h b/chrome/browser/ui/views/accessibility/caption_bubble.h
similarity index 77%
rename from chrome/browser/accessibility/caption_bubble.h
rename to chrome/browser/ui/views/accessibility/caption_bubble.h
index 7af941e7..504ef63 100644
--- a/chrome/browser/accessibility/caption_bubble.h
+++ b/chrome/browser/ui/views/accessibility/caption_bubble.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef CHROME_BROWSER_ACCESSIBILITY_CAPTION_BUBBLE_H_
-#define CHROME_BROWSER_ACCESSIBILITY_CAPTION_BUBBLE_H_
+#ifndef CHROME_BROWSER_UI_VIEWS_ACCESSIBILITY_CAPTION_BUBBLE_H_
+#define CHROME_BROWSER_UI_VIEWS_ACCESSIBILITY_CAPTION_BUBBLE_H_
 
 #include <string>
 
@@ -22,6 +22,8 @@
  public:
   explicit CaptionBubble(views::View* anchor);
   ~CaptionBubble() override;
+  CaptionBubble(const CaptionBubble&) = delete;
+  CaptionBubble& operator=(const CaptionBubble&) = delete;
 
   // Create and show the caption bubble.
   static void CreateAndShow(views::View* anchor);
@@ -36,11 +38,9 @@
       views::Widget* widget) override;
 
  private:
-  views::Label* label_;
-
-  DISALLOW_COPY_AND_ASSIGN(CaptionBubble);
+  views::Label label_;
 };
 
 }  // namespace captions
 
-#endif  // CHROME_BROWSER_ACCESSIBILITY_CAPTION_BUBBLE_H_
+#endif  // CHROME_BROWSER_UI_VIEWS_ACCESSIBILITY_CAPTION_BUBBLE_H_
diff --git a/chrome/browser/ui/views/frame/browser_view.cc b/chrome/browser/ui/views/frame/browser_view.cc
index 911d64e..cf46aff6 100644
--- a/chrome/browser/ui/views/frame/browser_view.cc
+++ b/chrome/browser/ui/views/frame/browser_view.cc
@@ -27,7 +27,6 @@
 #include "base/threading/thread_task_runner_handle.h"
 #include "build/build_config.h"
 #include "chrome/app/chrome_command_ids.h"
-#include "chrome/browser/accessibility/caption_bubble.h"
 #include "chrome/browser/app_mode/app_mode_utils.h"
 #include "chrome/browser/banners/app_banner_manager.h"
 #include "chrome/browser/browser_process.h"
@@ -74,6 +73,7 @@
 #include "chrome/browser/ui/ui_features.h"
 #include "chrome/browser/ui/view_ids.h"
 #include "chrome/browser/ui/views/accelerator_table.h"
+#include "chrome/browser/ui/views/accessibility/caption_bubble.h"
 #include "chrome/browser/ui/views/accessibility/invert_bubble_view.h"
 #include "chrome/browser/ui/views/autofill/autofill_bubble_handler_impl.h"
 #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h"
diff --git a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
index f9a76c94..006424a 100644
--- a/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
+++ b/chrome/browser/ui/views/frame/glass_browser_frame_view.cc
@@ -179,6 +179,8 @@
 
 bool GlassBrowserFrameView::HasVisibleBackgroundTabShapes(
     BrowserFrameActiveState active_state) const {
+  DCHECK(GetWidget());
+
   // Pre-Win 8, tabs never match the glass frame appearance.
   if (base::win::GetVersion() < base::win::Version::WIN8)
     return true;
@@ -189,7 +191,7 @@
   // colors).
   // TODO(pkasting): https://crbug.com/831769  Change the architecture of the
   // high contrast support to respect system colors, then remove this.
-  if (ui::NativeTheme::GetInstanceForNativeUi()->UsesHighContrastColors())
+  if (GetNativeTheme()->UsesHighContrastColors())
     return true;
 
   return BrowserNonClientFrameView::HasVisibleBackgroundTabShapes(active_state);
diff --git a/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view_unittest.cc b/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view_unittest.cc
index d0f63445..db647aa 100644
--- a/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view_unittest.cc
+++ b/chrome/browser/ui/views/media_router/cast_dialog_no_sinks_view_unittest.cc
@@ -44,7 +44,7 @@
   EXPECT_TRUE(looking_for_sinks_view()->GetVisible());
   EXPECT_FALSE(help_icon_view());
 
-  task_environment_.FastForwardBy(base::TimeDelta::FromSeconds(3));
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(3));
   // After three seconds, only the help icon view should be shown.
   EXPECT_FALSE(looking_for_sinks_view());
   EXPECT_TRUE(help_icon_view()->GetVisible());
diff --git a/chrome/browser/ui/views/sharing/remote_copy_browsertest.cc b/chrome/browser/ui/views/sharing/remote_copy_browsertest.cc
index d8291d2..b051834 100644
--- a/chrome/browser/ui/views/sharing/remote_copy_browsertest.cc
+++ b/chrome/browser/ui/views/sharing/remote_copy_browsertest.cc
@@ -12,6 +12,7 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/test/metrics/histogram_tester.h"
 #include "base/test/scoped_feature_list.h"
+#include "build/build_config.h"
 #include "chrome/browser/notifications/notification_display_service_tester.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/sharing/shared_clipboard/feature_flags.h"
@@ -237,8 +238,14 @@
                 base::ASCIIToUTF16(kDeviceName)),
             notification.title());
   ASSERT_EQ(message_center::NOTIFICATION_TYPE_IMAGE, notification.type());
+#if defined(OS_MACOSX)
+  // We show the image in the notification icon on macOS.
+  ASSERT_EQ(640, notification.icon().Width());
+  ASSERT_EQ(480, notification.icon().Height());
+#else
   ASSERT_EQ(640, notification.rich_notification_data().image.Width());
   ASSERT_EQ(480, notification.rich_notification_data().image.Height());
+#endif  // defined(OS_MACOSX)
   histograms_.ExpectUniqueSample(kStatusCodeHistogram, net::HTTP_OK, 1);
   histograms_.ExpectTotalCount(kLoadTimeHistogram, 1);
   histograms_.ExpectUniqueSample(kImageSizeBeforeDecodeHistogram, 810490, 1);
diff --git a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
index a4a8244b..2f620b63 100644
--- a/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
+++ b/chrome/browser/ui/views/tabs/tab_strip_unittest.cc
@@ -1035,13 +1035,13 @@
   const int initial_width = tab_strip_->tab_at(1)->width();
   EXPECT_LT(initial_width, tab_strip_->tab_at(0)->width());
 
-  task_environment_.FastForwardBy(TabAnimation::kAnimationDuration / 2);
+  task_environment()->FastForwardBy(TabAnimation::kAnimationDuration / 2);
 
   EXPECT_GT(tab_strip_->tab_at(1)->width(), initial_width);
   EXPECT_LT(tab_strip_->tab_at(1)->width(), tab_strip_->tab_at(0)->width());
 
   // Fast-forward by more than enough to ensure the animation finishes.
-  task_environment_.FastForwardBy(TabAnimation::kAnimationDuration);
+  task_environment()->FastForwardBy(TabAnimation::kAnimationDuration);
 
   EXPECT_EQ(tab_strip_->tab_at(0)->width(), tab_strip_->tab_at(1)->width());
 }
diff --git a/chrome/browser/upboarding/query_tiles/android/tile_provider_bridge.cc b/chrome/browser/upboarding/query_tiles/android/tile_provider_bridge.cc
index e971c1535..117d7dd9 100644
--- a/chrome/browser/upboarding/query_tiles/android/tile_provider_bridge.cc
+++ b/chrome/browser/upboarding/query_tiles/android/tile_provider_bridge.cc
@@ -4,6 +4,10 @@
 
 #include "chrome/browser/upboarding/query_tiles/android/tile_provider_bridge.h"
 
+#include <memory>
+#include <string>
+#include <vector>
+
 #include "base/android/callback_android.h"
 #include "base/android/jni_string.h"
 #include "chrome/browser/upboarding/query_tiles/jni_headers/TileProviderBridge_jni.h"
@@ -26,8 +30,8 @@
   ScopedJavaLocalRef<jobject> jchildren =
       Java_TileProviderBridge_createList(env);
 
-  for (QueryTileEntry* subtile : tile->subtiles)
-    createJavaTileAndMaybeAddToList(env, jchildren, subtile);
+  for (const auto& subtile : tile->sub_tiles)
+    createJavaTileAndMaybeAddToList(env, jchildren, subtile.get());
 
   return Java_TileProviderBridge_createTileAndMaybeAddToList(
       env, jlist, ConvertUTF8ToJavaString(env, tile->id),
diff --git a/chrome/browser/upboarding/query_tiles/internal/BUILD.gn b/chrome/browser/upboarding/query_tiles/internal/BUILD.gn
index 55d0412..f6b467a 100644
--- a/chrome/browser/upboarding/query_tiles/internal/BUILD.gn
+++ b/chrome/browser/upboarding/query_tiles/internal/BUILD.gn
@@ -63,6 +63,7 @@
     "//chrome/browser/upboarding/query_tiles:public",
     "//chrome/browser/upboarding/query_tiles/internal",
     "//chrome/browser/upboarding/query_tiles/proto",
+    "//chrome/browser/upboarding/query_tiles/test:test_lib",
     "//components/image_fetcher/core:test_support",
     "//components/leveldb_proto",
     "//components/leveldb_proto:test_support",
diff --git a/chrome/browser/upboarding/query_tiles/internal/proto_conversion.cc b/chrome/browser/upboarding/query_tiles/internal/proto_conversion.cc
index 4eb874e..dae083ef 100644
--- a/chrome/browser/upboarding/query_tiles/internal/proto_conversion.cc
+++ b/chrome/browser/upboarding/query_tiles/internal/proto_conversion.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/upboarding/query_tiles/internal/proto_conversion.h"
 
+#include <memory>
+#include <utility>
+
 #include "base/strings/utf_string_conversions.h"
 
 namespace upboarding {
@@ -11,6 +14,8 @@
 void QueryTileEntryToProto(
     upboarding::QueryTileEntry* entry,
     upboarding::query_tiles::proto::QueryTileEntry* proto) {
+  DCHECK(entry);
+  DCHECK(proto);
   proto->set_id(entry->id);
   proto->set_query_text(entry->query_text);
   proto->set_display_text(entry->display_text);
@@ -23,15 +28,17 @@
     data->set_url(image.url.spec());
   }
 
-  // Set Ids of children.
-  for (const auto& child : entry->children) {
-    proto->add_children(child);
+  // Set children.
+  for (auto& subtile : entry->sub_tiles) {
+    QueryTileEntryToProto(subtile.get(), proto->add_sub_tiles());
   }
 }
 
 void QueryTileEntryFromProto(
     upboarding::query_tiles::proto::QueryTileEntry* proto,
     upboarding::QueryTileEntry* entry) {
+  DCHECK(entry);
+  DCHECK(proto);
   entry->id = proto->id();
   entry->query_text = proto->query_text();
   entry->display_text = proto->display_text();
@@ -41,8 +48,11 @@
     entry->image_metadatas.emplace_back(image_md.id(), GURL(image_md.url()));
   }
 
-  for (const auto& child : proto->children()) {
-    entry->children.emplace(child);
+  for (int i = 0; i < proto->sub_tiles_size(); i++) {
+    auto sub_tile_proto = proto->sub_tiles(i);
+    auto child = std::make_unique<QueryTileEntry>();
+    QueryTileEntryFromProto(&sub_tile_proto, child.get());
+    entry->sub_tiles.emplace_back(std::move(child));
   }
 }
 
diff --git a/chrome/browser/upboarding/query_tiles/internal/proto_conversion_unittest.cc b/chrome/browser/upboarding/query_tiles/internal/proto_conversion_unittest.cc
index f313608..5862253 100644
--- a/chrome/browser/upboarding/query_tiles/internal/proto_conversion_unittest.cc
+++ b/chrome/browser/upboarding/query_tiles/internal/proto_conversion_unittest.cc
@@ -4,27 +4,31 @@
 
 #include "chrome/browser/upboarding/query_tiles/internal/proto_conversion.h"
 
+#include <memory>
 #include <string>
 #include <utility>
 #include <vector>
 
+#include "chrome/browser/upboarding/query_tiles/test/test_utils.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
 namespace upboarding {
 namespace {
 
-void TestQueryTileEntryConversion(QueryTileEntry* expected) {
-  DCHECK(expected);
+void TestQueryTileEntryConversion(QueryTileEntry& expected) {
   upboarding::query_tiles::proto::QueryTileEntry proto;
   QueryTileEntry actual;
-  QueryTileEntryToProto(expected, &proto);
+  QueryTileEntryToProto(&expected, &proto);
   QueryTileEntryFromProto(&proto, &actual);
-  EXPECT_EQ(*expected, actual);
+  EXPECT_TRUE(expected == actual)
+      << "actual: \n"
+      << test::DebugString(&actual) << "expected: \n"
+      << test::DebugString(&expected);
 }
 
 TEST(ProtoConversionTest, QueryTileEntryConversion) {
   QueryTileEntry entry;
-  entry.id = "test-guid-001";
+  entry.id = "test-guid-root";
   entry.query_text = "test query str";
   entry.display_text = "test display text";
   entry.accessibility_text = "read this test display text";
@@ -32,10 +36,16 @@
                                      GURL("www.example.com"));
   entry.image_metadatas.emplace_back("image-test-id-2",
                                      GURL("www.fakeurl.com"));
-  entry.children.emplace("test-guid-002");
-  entry.children.emplace("test-guid-003");
-  entry.children.emplace("test-guid-004");
-  TestQueryTileEntryConversion(&entry);
+  auto entry1 = std::make_unique<QueryTileEntry>();
+  entry1->id = "test-guid-001";
+  auto entry2 = std::make_unique<QueryTileEntry>();
+  entry2->id = "test-guid-002";
+  auto entry3 = std::make_unique<QueryTileEntry>();
+  entry3->id = "test-guid-003";
+  entry1->sub_tiles.emplace_back(std::move(entry3));
+  entry.sub_tiles.emplace_back(std::move(entry1));
+  entry.sub_tiles.emplace_back(std::move(entry2));
+  TestQueryTileEntryConversion(entry);
 }
 
 }  // namespace
diff --git a/chrome/browser/upboarding/query_tiles/internal/query_tile_store.cc b/chrome/browser/upboarding/query_tiles/internal/query_tile_store.cc
index 016057a..f75bdda 100644
--- a/chrome/browser/upboarding/query_tiles/internal/query_tile_store.cc
+++ b/chrome/browser/upboarding/query_tiles/internal/query_tile_store.cc
@@ -38,7 +38,8 @@
                             const QueryTileEntry& entry,
                             UpdateCallback callback) {
   auto entries_to_save = std::make_unique<KeyEntryVector>();
-  entries_to_save->emplace_back(key, entry);
+  QueryTileEntry entry_to_save = entry;
+  entries_to_save->emplace_back(key, std::move(entry_to_save));
   db_->UpdateEntries(std::move(entries_to_save),
                      std::make_unique<KeyVector>() /*keys_to_remove*/,
                      std::move(callback));
@@ -75,8 +76,8 @@
 
   KeysAndEntries keys_and_entries;
   for (auto& it : *loaded_keys_and_entries) {
-    std::unique_ptr<QueryTileEntry> entry = std::make_unique<QueryTileEntry>();
-    *entry = std::move(it.second);
+    std::unique_ptr<QueryTileEntry> entry =
+        std::make_unique<QueryTileEntry>(std::move(it.second));
     keys_and_entries.emplace(it.first, std::move(entry));
   }
 
diff --git a/chrome/browser/upboarding/query_tiles/internal/query_tile_store_unittest.cc b/chrome/browser/upboarding/query_tiles/internal/query_tile_store_unittest.cc
index 63b9e02f..12f3e36 100644
--- a/chrome/browser/upboarding/query_tiles/internal/query_tile_store_unittest.cc
+++ b/chrome/browser/upboarding/query_tiles/internal/query_tile_store_unittest.cc
@@ -11,6 +11,7 @@
 
 #include "base/test/task_environment.h"
 #include "chrome/browser/upboarding/query_tiles/internal/proto_conversion.h"
+#include "chrome/browser/upboarding/query_tiles/test/test_utils.h"
 #include "components/leveldb_proto/public/proto_database.h"
 #include "components/leveldb_proto/testing/fake_db.h"
 #include "testing/gtest/include/gtest/gtest.h"
@@ -21,6 +22,9 @@
 namespace upboarding {
 namespace {
 
+const char kGuid[] = "test_guid";
+const char kTestDisplayText[] = "test_display_text";
+
 class QueryTileStoreTest : public testing::Test {
  public:
   using QueryTileEntryProto = query_tiles::proto::QueryTileEntry;
@@ -32,8 +36,6 @@
   QueryTileStoreTest() : load_result_(false), db_(nullptr) {}
   ~QueryTileStoreTest() override = default;
 
-  void SetUp() override {}
-
   QueryTileStoreTest(const QueryTileStoreTest& other) = delete;
   QueryTileStoreTest& operator=(const QueryTileStoreTest& other) = delete;
 
@@ -55,7 +57,7 @@
   }
 
   void CreateTestDbEntries(TestEntries input) {
-    for (auto entry : input) {
+    for (auto& entry : input) {
       QueryTileEntryProto proto;
       upboarding::QueryTileEntryToProto(&entry, &proto);
       db_entries_.emplace(entry.id, proto);
@@ -76,7 +78,14 @@
     EXPECT_TRUE(success);
     DCHECK(expected);
     DCHECK(loaded_entries);
-    EXPECT_EQ(*expected.get(), *loaded_entries.get());
+    for (auto it = loaded_entries->begin(); it != loaded_entries->end(); it++) {
+      EXPECT_NE(expected->count(it->first), 0u);
+      auto& actual_loaded_tree = it->second;
+      auto& expected_tree = expected->at(it->first);
+      EXPECT_EQ(actual_loaded_tree, expected_tree)
+          << "\n Actual: " << test::DebugString(&actual_loaded_tree)
+          << "\n Expected: " << test::DebugString(&expected_tree);
+    }
   }
 
   bool load_result() const { return load_result_; }
@@ -96,7 +105,7 @@
 // Test Initializing and loading an empty database .
 TEST_F(QueryTileStoreTest, InitSuccessEmptyDb) {
   auto test_data = TestEntries();
-  Init(test_data, InitStatus::kOK);
+  Init(std::move(test_data), InitStatus::kOK);
   db()->LoadCallback(true);
   EXPECT_EQ(load_result(), true);
   EXPECT_TRUE(in_memory_entries().empty());
@@ -105,81 +114,89 @@
 // Test Initializing and loading a non-empty database.
 TEST_F(QueryTileStoreTest, InitSuccessWithData) {
   auto test_data = TestEntries();
-  auto test_entry = QueryTileEntry();
-  test_entry.id = "test-id";
-  test_data.emplace_back(test_entry);
-  Init(test_data, InitStatus::kOK);
+  QueryTileEntry test_entry;
+  test_entry.id = kGuid;
+  test_data.emplace_back(std::move(test_entry));
+  Init(std::move(test_data), InitStatus::kOK);
   db()->LoadCallback(true);
   EXPECT_EQ(load_result(), true);
   EXPECT_EQ(in_memory_entries().size(), 1u);
   auto actual = in_memory_entries().begin();
-  EXPECT_EQ(actual->first, test_entry.id);
-  EXPECT_EQ(*(actual->second.get()), test_entry);
+  EXPECT_EQ(actual->first, kGuid);
+  EXPECT_EQ(actual->second.get()->id, kGuid);
 }
 
 // Test Initializing and loading a non-empty database failed.
 TEST_F(QueryTileStoreTest, InitFailedWithData) {
   auto test_data = TestEntries();
-  auto test_entry = QueryTileEntry();
-  test_entry.id = "test-id";
-  test_data.emplace_back(test_entry);
-  Init(test_data, InitStatus::kOK);
+  QueryTileEntry test_entry;
+  test_entry.id = kGuid;
+  test_data.emplace_back(std::move(test_entry));
+  Init(std::move(test_data), InitStatus::kOK);
   db()->LoadCallback(false);
   EXPECT_EQ(load_result(), false);
   EXPECT_TRUE(in_memory_entries().empty());
 }
 
 // Test adding and updating.
-TEST_F(QueryTileStoreTest, AddAndUpdateDataSuccess) {
+TEST_F(QueryTileStoreTest, AddAndUpdateDataFailed) {
   auto test_data = TestEntries();
-  Init(test_data, InitStatus::kOK);
+  Init(std::move(test_data), InitStatus::kOK);
   db()->LoadCallback(true);
   EXPECT_EQ(load_result(), true);
   EXPECT_TRUE(in_memory_entries().empty());
 
   // Add an entry failed.
-  auto test_entry_1 = QueryTileEntry();
+  QueryTileEntry test_entry_1;
   test_entry_1.id = "test_entry_id_1";
   test_entry_1.display_text = "test_entry_test_display_text";
   store()->Update(test_entry_1.id, test_entry_1,
                   base::BindOnce([](bool success) { EXPECT_FALSE(success); }));
   db()->UpdateCallback(false);
+}
+
+TEST_F(QueryTileStoreTest, AddAndUpdateDataSuccess) {
+  auto test_data = TestEntries();
+  Init(std::move(test_data), InitStatus::kOK);
+  db()->LoadCallback(true);
+  EXPECT_EQ(load_result(), true);
+  EXPECT_TRUE(in_memory_entries().empty());
 
   // Add an entry successfully.
+  QueryTileEntry test_entry_1;
+  test_entry_1.id = "test_entry_id_1";
+  test_entry_1.display_text = kTestDisplayText;
+  auto test_entry_2 = std::make_unique<QueryTileEntry>();
+  test_entry_2->id = "test_entry_id_2";
+  test_entry_1.sub_tiles.emplace_back(std::move(test_entry_2));
   store()->Update(test_entry_1.id, test_entry_1,
                   base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
   db()->UpdateCallback(true);
 
   auto expected = std::make_unique<KeysAndEntries>();
-  expected->emplace(test_entry_1.id, test_entry_1);
-  VerifyDataInDb(std::move(expected));
-
-  // Update an existing entry successfully.
-  test_entry_1.display_text = "test_entry_test_display_text_updated";
-  store()->Update(test_entry_1.id, test_entry_1,
-                  base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
-  db()->UpdateCallback(true);
-
-  expected = std::make_unique<KeysAndEntries>();
-  expected->emplace(test_entry_1.id, test_entry_1);
+  expected->emplace(test_entry_1.id, std::move(test_entry_1));
   VerifyDataInDb(std::move(expected));
 }
 
-// Test Deleting from db.
+// Test deleting from db.
 TEST_F(QueryTileStoreTest, DeleteSuccess) {
   auto test_data = TestEntries();
-  auto test_entry = QueryTileEntry();
-  test_entry.id = "test-id";
-  test_data.emplace_back(test_entry);
-  Init(test_data, InitStatus::kOK);
+  QueryTileEntry test_entry_1;
+  test_entry_1.id = kGuid;
+  test_entry_1.display_text = kTestDisplayText;
+  auto test_entry_2 = std::make_unique<QueryTileEntry>();
+  test_entry_2->id = "test_entry_id_2";
+  test_entry_1.sub_tiles.emplace_back(std::move(test_entry_2));
+  test_data.emplace_back(std::move(test_entry_1));
+  Init(std::move(test_data), InitStatus::kOK);
   db()->LoadCallback(true);
   EXPECT_EQ(load_result(), true);
   EXPECT_EQ(in_memory_entries().size(), 1u);
   auto actual = in_memory_entries().begin();
-  EXPECT_EQ(actual->first, test_entry.id);
-  EXPECT_EQ(*(actual->second.get()), test_entry);
+  EXPECT_EQ(actual->first, kGuid);
+  EXPECT_EQ(actual->second.get()->id, kGuid);
 
-  store()->Delete(test_entry.id,
+  store()->Delete(kGuid,
                   base::BindOnce([](bool success) { EXPECT_TRUE(success); }));
   db()->UpdateCallback(true);
   // No entry is expected in db.
diff --git a/chrome/browser/upboarding/query_tiles/proto/query_tile_entry.proto b/chrome/browser/upboarding/query_tiles/proto/query_tile_entry.proto
index b9b5865..bd5e358 100644
--- a/chrome/browser/upboarding/query_tiles/proto/query_tile_entry.proto
+++ b/chrome/browser/upboarding/query_tiles/proto/query_tile_entry.proto
@@ -36,6 +36,6 @@
   // A list of image metadatas.
   repeated ImageMetadata image_metadatas = 5;
 
-  // Sub-level QueryTiles id;
-  repeated string children = 6;
+  // Sub level children.
+  repeated QueryTileEntry sub_tiles = 6;
 }
diff --git a/chrome/browser/upboarding/query_tiles/query_tile_entry.cc b/chrome/browser/upboarding/query_tiles/query_tile_entry.cc
index fd80e15..c9ea00f 100644
--- a/chrome/browser/upboarding/query_tiles/query_tile_entry.cc
+++ b/chrome/browser/upboarding/query_tiles/query_tile_entry.cc
@@ -4,7 +4,60 @@
 
 #include "chrome/browser/upboarding/query_tiles/query_tile_entry.h"
 
+#include <utility>
+
 namespace upboarding {
+namespace {
+
+void DeepCopyTiles(const QueryTileEntry* input, QueryTileEntry* out) {
+  if (!input || !out)
+    return;
+
+  out->id = input->id;
+  out->display_text = input->display_text;
+  out->query_text = input->query_text;
+  out->accessibility_text = input->accessibility_text;
+  out->image_metadatas = input->image_metadatas;
+  out->sub_tiles.clear();
+  for (const auto& child : input->sub_tiles) {
+    auto entry = std::make_unique<QueryTileEntry>();
+    DeepCopyTiles(child.get(), entry.get());
+    out->sub_tiles.emplace_back(std::move(entry));
+  }
+}
+
+bool AreTreesIdentical(const QueryTileEntry* lhs, const QueryTileEntry* rhs) {
+  if (!lhs && !rhs)
+    return true;
+  if (!lhs || !rhs || lhs->id != rhs->id ||
+      lhs->display_text != rhs->display_text ||
+      lhs->query_text != rhs->query_text ||
+      lhs->accessibility_text != rhs->accessibility_text)
+    return false;
+
+  for (const auto& it : lhs->image_metadatas) {
+    auto found =
+        std::find_if(rhs->image_metadatas.begin(), rhs->image_metadatas.end(),
+                     [it](const ImageMetadata& image) { return image == it; });
+    if (found == rhs->image_metadatas.end())
+      return false;
+  }
+
+  for (auto& it : lhs->sub_tiles) {
+    auto* target = it.get();
+    auto found =
+        std::find_if(rhs->sub_tiles.begin(), rhs->sub_tiles.end(),
+                     [&target](const std::unique_ptr<QueryTileEntry>& entry) {
+                       return entry->id == target->id;
+                     });
+    if (found == rhs->sub_tiles.end() ||
+        !AreTreesIdentical(target, found->get()))
+      return false;
+  }
+  return true;
+}
+
+}  // namespace
 
 ImageMetadata::ImageMetadata() = default;
 
@@ -23,13 +76,24 @@
 
 QueryTileEntry::~QueryTileEntry() = default;
 
-QueryTileEntry::QueryTileEntry(const QueryTileEntry& other) = default;
+QueryTileEntry::QueryTileEntry(const QueryTileEntry& other) {
+  DeepCopyTiles(&other, this);
+}
+
+QueryTileEntry::QueryTileEntry(QueryTileEntry&& other) {
+  id = std::move(other.id);
+  query_text = std::move(other.query_text);
+  display_text = std::move(other.display_text);
+  accessibility_text = std::move(other.accessibility_text);
+  image_metadatas = std::move(other.image_metadatas);
+  sub_tiles = std::move(other.sub_tiles);
+}
 
 bool QueryTileEntry::operator==(const QueryTileEntry& other) const {
-  return id == other.id && query_text == other.query_text &&
-         display_text == other.display_text &&
-         accessibility_text == other.accessibility_text &&
-         image_metadatas == other.image_metadatas && children == other.children;
+  return AreTreesIdentical(this, &other);
+}
+bool QueryTileEntry::operator!=(const QueryTileEntry& other) const {
+  return !(*this == other);
 }
 
 }  // namespace upboarding
diff --git a/chrome/browser/upboarding/query_tiles/query_tile_entry.h b/chrome/browser/upboarding/query_tiles/query_tile_entry.h
index cee8aaa..1424e70d 100644
--- a/chrome/browser/upboarding/query_tiles/query_tile_entry.h
+++ b/chrome/browser/upboarding/query_tiles/query_tile_entry.h
@@ -5,7 +5,7 @@
 #ifndef CHROME_BROWSER_UPBOARDING_QUERY_TILES_QUERY_TILE_ENTRY_H_
 #define CHROME_BROWSER_UPBOARDING_QUERY_TILES_QUERY_TILE_ENTRY_H_
 
-#include <set>
+#include <memory>
 #include <string>
 #include <vector>
 
@@ -33,9 +33,11 @@
 struct QueryTileEntry {
   QueryTileEntry();
   ~QueryTileEntry();
-  QueryTileEntry(const QueryTileEntry& other);
   bool operator==(const QueryTileEntry& other) const;
+  bool operator!=(const QueryTileEntry& other) const;
 
+  QueryTileEntry(const QueryTileEntry& other);
+  QueryTileEntry(QueryTileEntry&& other);
   // Unique Id for each entry.
   std::string id;
 
@@ -51,11 +53,8 @@
   // A list of images's matadatas.
   std::vector<ImageMetadata> image_metadatas;
 
-  // A set contains all ids of it's children tiles.
-  std::set<std::string> children;
-
-  // TODO(shaktisahu): Merge children into subtiles.
-  std::vector<QueryTileEntry*> subtiles;
+  // A list of children of this tile.
+  std::vector<std::unique_ptr<QueryTileEntry>> sub_tiles;
 };
 
 }  // namespace upboarding
diff --git a/chrome/browser/upboarding/query_tiles/test/BUILD.gn b/chrome/browser/upboarding/query_tiles/test/BUILD.gn
new file mode 100644
index 0000000..c18e50c
--- /dev/null
+++ b/chrome/browser/upboarding/query_tiles/test/BUILD.gn
@@ -0,0 +1,24 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("test_lib") {
+  testonly = true
+
+  visibility = [
+    "//chrome/browser/upboarding/query_tiles/internal:unit_tests",
+    "//chrome/browser/upboarding/query_tiles:unit_tests",
+  ]
+
+  sources = [
+    "test_utils.cc",
+    "test_utils.h",
+  ]
+
+  deps = [
+    "//chrome/browser/upboarding/query_tiles:public",
+    "//chrome/browser/upboarding/query_tiles/internal",
+    "//testing/gmock",
+    "//testing/gtest",
+  ]
+}
diff --git a/chrome/browser/upboarding/query_tiles/test/test_utils.cc b/chrome/browser/upboarding/query_tiles/test/test_utils.cc
new file mode 100644
index 0000000..84ab560
--- /dev/null
+++ b/chrome/browser/upboarding/query_tiles/test/test_utils.cc
@@ -0,0 +1,67 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/upboarding/query_tiles/test/test_utils.h"
+
+#include <algorithm>
+#include <deque>
+#include <map>
+#include <memory>
+#include <sstream>
+#include <vector>
+
+namespace upboarding {
+namespace test {
+
+namespace {
+void SerializeEntry(const QueryTileEntry* entry, std::stringstream& out) {
+  if (!entry)
+    return;
+  out << "entry id: " << entry->id << " query text: " << entry->query_text
+      << "  display text: " << entry->display_text
+      << "  accessibility_text: " << entry->accessibility_text << " \n";
+
+  for (const auto& image : entry->image_metadatas)
+    out << "image id: " << image.id << " image url: " << image.url.spec()
+        << " \n";
+}
+
+}  // namespace
+
+const std::string DebugString(const QueryTileEntry* root) {
+  if (!root)
+    return std::string();
+  std::stringstream out;
+  out << "entries detail: \n";
+  std::map<std::string, std::vector<std::string>> cache;
+  std::deque<const QueryTileEntry*> queue;
+  queue.emplace_back(root);
+  while (!queue.empty()) {
+    size_t size = queue.size();
+    for (size_t i = 0; i < size; i++) {
+      auto* parent = queue.front();
+      SerializeEntry(parent, out);
+      queue.pop_front();
+      for (size_t j = 0; j < parent->sub_tiles.size(); j++) {
+        cache[parent->id].emplace_back(parent->sub_tiles[j]->id);
+        queue.emplace_back(parent->sub_tiles[j].get());
+      }
+    }
+  }
+  out << "tree table: \n";
+  for (auto& pair : cache) {
+    std::string line;
+    line += pair.first + " : [";
+    std::sort(pair.second.begin(), pair.second.end());
+    for (const auto& child : pair.second)
+      line += " " + child;
+    line += " ]\n";
+    out << line;
+  }
+  return out.str();
+}
+
+}  // namespace test
+
+}  // namespace upboarding
diff --git a/chrome/browser/upboarding/query_tiles/test/test_utils.h b/chrome/browser/upboarding/query_tiles/test/test_utils.h
new file mode 100644
index 0000000..06712cfe
--- /dev/null
+++ b/chrome/browser/upboarding/query_tiles/test/test_utils.h
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_UPBOARDING_QUERY_TILES_TEST_TEST_UTILS_H_
+#define CHROME_BROWSER_UPBOARDING_QUERY_TILES_TEST_TEST_UTILS_H_
+
+#include <string>
+
+#include "chrome/browser/upboarding/query_tiles/query_tile_entry.h"
+
+namespace upboarding {
+namespace test {
+
+// Print data in QueryTileEntry, also with tree represent by adjacent nodes
+// key-value[parent id: {children id}] pairs.
+const std::string DebugString(const QueryTileEntry* entry);
+
+}  // namespace test
+
+}  // namespace upboarding
+
+#endif  // CHROME_BROWSER_UPBOARDING_QUERY_TILES_TEST_TEST_UTILS_H_
diff --git a/chrome/test/base/browser_with_test_window_test.cc b/chrome/test/base/browser_with_test_window_test.cc
index de9b6c0..d338887 100644
--- a/chrome/test/base/browser_with_test_window_test.cc
+++ b/chrome/test/base/browser_with_test_window_test.cc
@@ -28,11 +28,9 @@
 
 #if defined(TOOLKIT_VIEWS)
 #include "chrome/browser/ui/views/chrome_constrained_window_views_client.h"
-#include "chrome/test/views/chrome_test_views_delegate.h"
 #include "components/constrained_window/constrained_window_views.h"
 
 #if defined(OS_CHROMEOS)
-#include "ash/test/ash_test_views_delegate.h"
 #include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
 #include "content/public/browser/context_factory.h"
 #endif
@@ -48,16 +46,11 @@
 void BrowserWithTestWindowTest::SetUp() {
   testing::Test::SetUp();
 #if defined(OS_CHROMEOS)
-  test_views_delegate_ =
-      std::make_unique<ChromeTestViewsDelegate<ash::AshTestViewsDelegate>>();
   ash_test_helper_.SetUp();
-#elif defined(TOOLKIT_VIEWS)
-  views_test_helper_.reset(new views::ScopedViewsTestHelper(
-      std::make_unique<ChromeTestViewsDelegate<>>()));
 #endif
 
-  // This must be created after ash_test_helper_ is set up so that it doesn't
-  // create an DeviceDataManager.
+  // This must be created after |ash_test_helper_| is set up so that it doesn't
+  // create a DeviceDataManager.
   rvh_test_enabler_ = std::make_unique<content::RenderViewHostTestEnabler>();
 
 #if defined(TOOLKIT_VIEWS)
diff --git a/chrome/test/base/browser_with_test_window_test.h b/chrome/test/base/browser_with_test_window_test.h
index b9f0f34..63ae6b4 100644
--- a/chrome/test/base/browser_with_test_window_test.h
+++ b/chrome/test/base/browser_with_test_window_test.h
@@ -19,6 +19,8 @@
 #include "testing/gtest/include/gtest/gtest.h"
 
 #if defined(TOOLKIT_VIEWS)
+#include "chrome/test/views/chrome_test_views_delegate.h"
+
 #if defined(OS_CHROMEOS)
 #include "ash/test/ash_test_helper.h"
 #include "ash/test/ash_test_views_delegate.h"
@@ -36,12 +38,6 @@
 
 class GURL;
 
-#if defined(TOOLKIT_VIEWS)
-namespace views {
-class TestViewsDelegate;
-}  // namespace views
-#endif
-
 namespace content {
 class NavigationController;
 }
@@ -226,9 +222,12 @@
 
 #if defined(OS_CHROMEOS)
   ash::AshTestHelper ash_test_helper_;
-  std::unique_ptr<views::TestViewsDelegate> test_views_delegate_;
+  std::unique_ptr<views::TestViewsDelegate> test_views_delegate_ =
+      std::make_unique<ChromeTestViewsDelegate<ash::AshTestViewsDelegate>>();
 #elif defined(TOOLKIT_VIEWS)
-  std::unique_ptr<views::ScopedViewsTestHelper> views_test_helper_;
+  std::unique_ptr<views::ScopedViewsTestHelper> views_test_helper_ =
+      std::make_unique<views::ScopedViewsTestHelper>(
+          std::make_unique<ChromeTestViewsDelegate<>>());
 #endif
 
   // The existence of this object enables tests via RenderViewHostTester.
diff --git a/chrome/test/data/perf/throughput_test_cases/main-impl-animations-throughput.html b/chrome/test/data/perf/throughput_test_cases/main-impl-animations-throughput.html
index 4c751a12..d516719 100644
--- a/chrome/test/data/perf/throughput_test_cases/main-impl-animations-throughput.html
+++ b/chrome/test/data/perf/throughput_test_cases/main-impl-animations-throughput.html
@@ -53,6 +53,12 @@
   window.location.hash.length > 1 ? + window.location.hash.substr(1) : 0;
 const skipEvery = targetMainFps > 0 ? parseInt(60 / targetMainFps) : 0;
 
+const query = window.location.search.substr(1).split('&');
+const shouldJank = query.indexOf('jank') >= 0;
+// Delay the start of the main thread animation by 2 seconds to test the
+// aggregation of the main and compositor thread throughput.
+const shouldDelay = query.indexOf('delay') >= 0;
+
 var currentValue = 0;
 const maxValue = 150;
 const valueChange = 5 * skipEvery;
@@ -63,9 +69,11 @@
 var animating = false;
 var incrementing = false;
 var frameCount = 0;
+var startTime;
 
 function animateStep() {
-  if (skipEvery && ++frameCount % skipEvery == 0) {
+  if (skipEvery && ++frameCount % skipEvery == 0 &&
+      (new Date() - startTime) >= 1000) {
     if (incrementing) {
       currentValue += valueChange;
       if (currentValue >= maxValue)
@@ -75,6 +83,10 @@
       if (currentValue <= minValue)
         incrementing = true;
     }
+    if (shouldJank) {
+      const now = new Date();
+      while ((new Date() - now) < 16) {}
+    }
     mainanim.style[attributeName] = setProp(currentValue);
   }
   if (animating)
@@ -83,6 +95,7 @@
 
 function startAnimation() {
   animating = true;
+  startTime = new Date();
   requestAnimationFrame(animateStep);
   cssanim.classList.remove('paused');
 }
diff --git a/chrome/test/data/webui/BUILD.gn b/chrome/test/data/webui/BUILD.gn
index 0fd5c8c..df201af 100644
--- a/chrome/test/data/webui/BUILD.gn
+++ b/chrome/test/data/webui/BUILD.gn
@@ -230,6 +230,7 @@
     "$root_gen_dir/chrome/test/data/webui/settings/collapse_radio_button_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/controlled_button_tests.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/controlled_radio_button_tests.m.js",
+    "$root_gen_dir/chrome/test/data/webui/settings/cookies_page_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/do_not_track_toggle_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/downloads_page_test.m.js",
     "$root_gen_dir/chrome/test/data/webui/settings/dropdown_menu_tests.m.js",
diff --git a/chrome/test/data/webui/media/media_feeds_webui_browsertest.js b/chrome/test/data/webui/media/media_feeds_webui_browsertest.js
index 72c293b..4add181e 100644
--- a/chrome/test/data/webui/media/media_feeds_webui_browsertest.js
+++ b/chrome/test/data/webui/media/media_feeds_webui_browsertest.js
@@ -6,6 +6,10 @@
  * @fileoverview Test suite for the Media Feeds WebUI.
  */
 
+const EXAMPLE_URL_1 = 'http://example.com/feed.json';
+
+GEN('#include "base/run_loop.h"');
+GEN('#include "chrome/browser/media/history/media_history_keyed_service.h"');
 GEN('#include "chrome/browser/ui/browser.h"');
 GEN('#include "media/base/media_switches.h"');
 
@@ -20,6 +24,97 @@
 
   isAsync: true,
 
+  testGenPreamble: function() {
+    GEN('auto* service =');
+    GEN('  media_history::MediaHistoryKeyedService::Get(');
+    GEN('    browser()->profile());');
+    GEN('service->DiscoverMediaFeed(GURL("' + EXAMPLE_URL_1 + '"));');
+    GEN('auto items = std::vector<media_feeds::mojom::MediaFeedItemPtr>();');
+    GEN('auto item = media_feeds::mojom::MediaFeedItem::New();');
+    GEN('item->name = base::ASCIIToUTF16("The Movie");');
+    GEN('item->type = media_feeds::mojom::MediaFeedItemType::kMovie;');
+    GEN('item->date_published = base::Time::FromDeltaSinceWindowsEpoch(');
+    GEN('  base::TimeDelta::FromMinutes(10));');
+    GEN('item->is_family_friendly = true;');
+    GEN('item->action_status =');
+    GEN('  media_feeds::mojom::MediaFeedItemActionStatus::kPotential;');
+    GEN('item->genre = base::ASCIIToUTF16("test");');
+    GEN('item->duration = base::TimeDelta::FromSeconds(30);');
+    GEN('item->live = media_feeds::mojom::LiveDetails::New();');
+    GEN('item->live->start_time = base::Time::FromDeltaSinceWindowsEpoch(');
+    GEN('  base::TimeDelta::FromMinutes(20));');
+    GEN('item->live->end_time = base::Time::FromDeltaSinceWindowsEpoch(');
+    GEN('  base::TimeDelta::FromMinutes(30));');
+    GEN('item->shown_count = 3;');
+    GEN('item->clicked = true;');
+    GEN('item->author = media_feeds::mojom::Author::New();');
+    GEN('item->author->name = "Media Site";');
+    GEN('item->author->url = GURL("https://www.example.com");');
+    GEN('item->action = media_feeds::mojom::Action::New();');
+    GEN('item->action->start_time = base::TimeDelta::FromSeconds(3);');
+    GEN('item->action->url = GURL("https://www.example.com");');
+    GEN('item->interaction_counters.emplace(');
+    GEN('  media_feeds::mojom::InteractionCounterType::kLike, 10000);');
+    GEN('item->interaction_counters.emplace(');
+    GEN('  media_feeds::mojom::InteractionCounterType::kDislike, 20000);');
+    GEN('item->interaction_counters.emplace(');
+    GEN('  media_feeds::mojom::InteractionCounterType::kWatch, 30000);');
+    GEN('item->content_ratings.push_back(');
+    GEN('  media_feeds::mojom::ContentRating::New("MPAA", "PG-13"));');
+    GEN('item->content_ratings.push_back(');
+    GEN('  media_feeds::mojom::ContentRating::New("agency", "TEST2"));');
+    GEN('item->identifiers.push_back(');
+    GEN('  media_feeds::mojom::Identifier::New(');
+    GEN('    media_feeds::mojom::Identifier::Type::kPartnerId, "TEST1"));');
+    GEN('item->identifiers.push_back(');
+    GEN('  media_feeds::mojom::Identifier::New(');
+    GEN('    media_feeds::mojom::Identifier::Type::kTMSId, "TEST2"));');
+    GEN('item->tv_episode = media_feeds::mojom::TVEpisode::New();');
+    GEN('item->tv_episode->name = "TV Episode Name";');
+    GEN('item->tv_episode->season_number = 1;');
+    GEN('item->tv_episode->episode_number = 2;');
+    GEN('item->tv_episode->identifiers.push_back(');
+    GEN('  media_feeds::mojom::Identifier::New(');
+    GEN('    media_feeds::mojom::Identifier::Type::kPartnerId, "TEST3"));');
+    GEN('item->play_next_candidate = ');
+    GEN('    media_feeds::mojom::PlayNextCandidate::New();');
+    GEN('item->play_next_candidate->name = "Next TV Episode Name";');
+    GEN('item->play_next_candidate->season_number = 1;');
+    GEN('item->play_next_candidate->episode_number = 3;');
+    GEN('item->play_next_candidate->duration =');
+    GEN('    base::TimeDelta::FromSeconds(10);');
+    GEN('item->play_next_candidate->action = ');
+    GEN('    media_feeds::mojom::Action::New();');
+    GEN('item->play_next_candidate->action->start_time =');
+    GEN('    base::TimeDelta::FromSeconds(3);');
+    GEN('item->play_next_candidate->action->url = ');
+    GEN('    GURL("https://www.example.com");');
+    GEN('item->play_next_candidate->identifiers.push_back(');
+    GEN('  media_feeds::mojom::Identifier::New(');
+    GEN('    media_feeds::mojom::Identifier::Type::kPartnerId, "TEST4"));');
+    GEN('media_session::MediaImage image1;');
+    GEN('image1.src = GURL("https://www.example.org/image1.png");');
+    GEN('item->images.push_back(image1);');
+    GEN('media_session::MediaImage image2;');
+    GEN('image2.src = GURL("https://www.example.org/image2.png");');
+    GEN('item->images.push_back(image2);');
+    GEN('items.push_back(std::move(item));');
+    GEN('std::vector<media_session::MediaImage> logos;');
+    GEN('media_session::MediaImage logo1;');
+    GEN('logo1.src = GURL("https://www.example.org/logo1.png");');
+    GEN('logos.push_back(logo1);');
+    GEN('media_session::MediaImage logo2;');
+    GEN('logo2.src = GURL("https://www.example.org/logo2.png");');
+    GEN('logos.push_back(logo2);');
+    GEN('service->StoreMediaFeedFetchResult(');
+    GEN('  1, std::move(items), media_feeds::mojom::FetchResult::kSuccess,');
+    GEN('  base::Time::FromDeltaSinceWindowsEpoch(');
+    GEN('  base::TimeDelta::FromMinutes(40)), logos, "Test Feed");');
+    GEN('base::RunLoop run_loop;');
+    GEN('service->PostTaskToDBForTest(run_loop.QuitClosure());');
+    GEN('run_loop.Run();');
+  },
+
   extraLibraries: [
     '//third_party/mocha/mocha.js',
     '//chrome/test/data/webui/mocha_adapter.js',
@@ -32,7 +127,7 @@
   });
 
   test('check feeds table is loaded', function() {
-    let feedHeaders =
+    const feedsHeaders =
         Array.from(document.querySelector('#feed-table-header').children);
 
     assertDeepEquals(
@@ -40,9 +135,90 @@
           'ID', 'Url', 'Display Name', 'Last Discovery Time', 'Last Fetch Time',
           'User Status', 'Last Fetch Result', 'Fetch Failed Count',
           'Cache Expiry Time', 'Last Fetch Item Count',
-          'Last Fetch Play Next Count', 'Last Fetch Content Types', 'Logos'
+          'Last Fetch Play Next Count', 'Last Fetch Content Types', 'Logos',
+          'Actions'
         ],
-        feedHeaders.map(x => x.textContent.trim()));
+        feedsHeaders.map(x => x.textContent.trim()));
+
+    const feedsContents =
+        document.querySelector('#feed-table-body').childNodes[0];
+
+    assertEquals('1', feedsContents.childNodes[0].textContent.trim());
+    assertEquals(EXAMPLE_URL_1, feedsContents.childNodes[1].textContent.trim());
+    assertEquals('Test Feed', feedsContents.childNodes[2].textContent.trim());
+    assertEquals('Auto', feedsContents.childNodes[5].textContent.trim());
+    assertEquals('Success', feedsContents.childNodes[6].textContent.trim());
+    assertEquals('0', feedsContents.childNodes[7].textContent.trim());
+    assertNotEquals('', feedsContents.childNodes[8].textContent.trim());
+    assertEquals('1', feedsContents.childNodes[9].textContent.trim());
+    assertEquals('1', feedsContents.childNodes[10].textContent.trim());
+    assertEquals('Movie', feedsContents.childNodes[11].textContent.trim());
+    assertEquals(
+        'https://www.example.org/logo1.pnghttps://www.example.org/logo2.png',
+        feedsContents.childNodes[12].textContent.trim());
+    assertEquals(
+        'Show Contents', feedsContents.childNodes[13].textContent.trim());
+
+    // Click on the show contents button.
+    feedsContents.childNodes[13].firstChild.click();
+
+    return whenFeedTableIsPopulatedForTest().then(() => {
+      assertEquals(
+          EXAMPLE_URL_1, document.querySelector('#current-feed').textContent);
+
+      const feedItemsHeaders = Array.from(
+          document.querySelector('#feed-items-table thead tr').children);
+
+      assertDeepEquals(
+          [
+            'Type', 'Name', 'Author', 'Date Published', 'Family Friendly',
+            'Action Status', 'Action URL', 'Action Start Time (secs)',
+            'Interaction Counters', 'Content Ratings', 'Genre', 'Live Details',
+            'TV Episode', 'Play Next Candidate', 'Identifiers', 'Shown Count',
+            'Clicked', 'Images'
+          ],
+          feedItemsHeaders.map(x => x.textContent.trim()));
+
+      const feedItemsContents =
+          document.querySelector('#feed-items-table tbody').childNodes[0];
+
+      assertEquals('Movie', feedItemsContents.childNodes[0].textContent.trim());
+      assertEquals(
+          'The Movie', feedItemsContents.childNodes[1].textContent.trim());
+      assertEquals(
+          'Media Site', feedItemsContents.childNodes[2].textContent.trim());
+      assertNotEquals('', feedItemsContents.childNodes[3].textContent.trim());
+      assertEquals('Yes', feedItemsContents.childNodes[4].textContent.trim());
+      assertEquals(
+          'Potential', feedItemsContents.childNodes[5].textContent.trim());
+      assertEquals(
+          'https://www.example.com/',
+          feedItemsContents.childNodes[6].textContent.trim());
+      assertEquals('3', feedItemsContents.childNodes[7].textContent.trim());
+      assertEquals(
+          'Watch=30000 Like=10000 Dislike=20000',
+          feedItemsContents.childNodes[8].textContent.trim());
+      assertEquals(
+          'MPAA PG-13, agency TEST2',
+          feedItemsContents.childNodes[9].textContent.trim());
+      assertEquals('test', feedItemsContents.childNodes[10].textContent.trim());
+      assertTrue(
+          feedItemsContents.childNodes[11].textContent.trim().includes('Live'));
+      assertEquals(
+          'TV Episode Name EpisodeNumber=2 SeasonNumber=1 PartnerId=TEST3',
+          feedItemsContents.childNodes[12].textContent.trim());
+      assertEquals(
+          'Next TV Episode Name EpisodeNumber=3 SeasonNumber=1 PartnerId=TEST4 ActionURL=https://www.example.com/ ActionStartTimeSecs=3 DurationSecs=10',
+          feedItemsContents.childNodes[13].textContent.trim());
+      assertEquals(
+          'PartnerId=TEST1 TMSId=TEST2',
+          feedItemsContents.childNodes[14].textContent.trim());
+      assertEquals('3', feedItemsContents.childNodes[15].textContent.trim());
+      assertEquals('Yes', feedItemsContents.childNodes[16].textContent.trim());
+      assertEquals(
+          'https://www.example.org/image1.pnghttps://www.example.org/image2.png',
+          feedItemsContents.childNodes[17].textContent.trim());
+    });
   });
 
   mocha.run();
diff --git a/chrome/test/data/webui/settings/BUILD.gn b/chrome/test/data/webui/settings/BUILD.gn
index 9e6e964..996a7a9f 100644
--- a/chrome/test/data/webui/settings/BUILD.gn
+++ b/chrome/test/data/webui/settings/BUILD.gn
@@ -24,6 +24,7 @@
     "collapse_radio_button_tests.js",
     "controlled_button_tests.js",
     "controlled_radio_button_tests.js",
+    "cookies_page_test.js",
     "do_not_track_toggle_test.js",
     "downloads_page_test.js",
     "dropdown_menu_tests.js",
diff --git a/chrome/test/data/webui/settings/cookies_page_test.js b/chrome/test/data/webui/settings/cookies_page_test.js
index 63d097b..c55a849 100644
--- a/chrome/test/data/webui/settings/cookies_page_test.js
+++ b/chrome/test/data/webui/settings/cookies_page_test.js
@@ -2,6 +2,16 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+// clang-format off
+// #import {ContentSetting, SiteSettingsPrefsBrowserProxyImpl, CookieControlsMode, ContentSettingsTypes, SiteSettingSource} from 'chrome://settings/lazy_load.js';
+// #import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
+// #import {MetricsBrowserProxyImpl, PrivacyElementInteractions} from 'chrome://settings/settings.js';
+// #import {TestMetricsBrowserProxy} from 'chrome://test/settings/test_metrics_browser_proxy.m.js';
+// #import {TestSiteSettingsPrefsBrowserProxy} from 'chrome://test/settings/test_site_settings_prefs_browser_proxy.m.js';
+// #import {createRawSiteException, createDefaultContentSetting,createSiteSettingsPrefs,createContentSettingTypeToValuePair} from 'chrome://test/settings/test_util.m.js';
+// #import {isChildVisible, flushTasks} from 'chrome://test/test_util.m.js';
+// clang-format on
+
 suite('CrSettingsCookiesPageTest', function() {
   /** @type {TestSiteSettingsPrefsBrowserProxy} */
   let siteSettingsBrowserProxy;
@@ -214,7 +224,7 @@
   });
 
   test('CookieSettingExceptions_Search', async function() {
-    exceptionPrefs = test_util.createSiteSettingsPrefs([], [
+    const exceptionPrefs = test_util.createSiteSettingsPrefs([], [
       test_util.createContentSettingTypeToValuePair(
           settings.ContentSettingsTypes.COOKIES,
           [
@@ -275,7 +285,7 @@
     Polymer.dom.flush();
 
     // Check the four radio buttons are correctly indicating they are managed.
-    for (button of radioButtons) {
+    for (const button of radioButtons) {
       assertTrue(button.disabled);
       assertEquals(button.policyIndicatorType, 'devicePolicy');
     }
@@ -317,7 +327,7 @@
     await siteSettingsBrowserProxy.whenCalled('getCookieControlsManagedState');
 
     // Check the four radio buttons no longer indicate they are managed.
-    for (button of radioButtons) {
+    for (const button of radioButtons) {
       assertFalse(button.disabled);
       assertEquals(button.policyIndicatorType, 'none');
     }
diff --git a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
index cc933050..ff26628 100644
--- a/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
+++ b/chrome/test/data/webui/settings/cr_settings_v3_browsertest.js
@@ -310,6 +310,7 @@
  ['CollapseRadioButton', 'collapse_radio_button_tests.m.js'],
  ['ControlledButton', 'controlled_button_tests.m.js'],
  ['ControlledRadioButton', 'controlled_radio_button_tests.m.js'],
+ ['CookiesPage', 'cookies_page_test.m.js'],
  ['DoNotTrackToggle', 'do_not_track_toggle_test.m.js'],
  ['DownloadsPage', 'downloads_page_test.m.js'],
  ['DropdownMenu', 'dropdown_menu_tests.m.js'],
diff --git a/chrome/test/views/chrome_views_test_base.cc b/chrome/test/views/chrome_views_test_base.cc
index 28ded8e..5f9cbfd 100644
--- a/chrome/test/views/chrome_views_test_base.cc
+++ b/chrome/test/views/chrome_views_test_base.cc
@@ -5,6 +5,13 @@
 #include "chrome/test/views/chrome_views_test_base.h"
 
 #include "chrome/test/views/chrome_test_views_delegate.h"
+#include "content/public/test/browser_task_environment.h"
+
+ChromeViewsTestBase::ChromeViewsTestBase()
+    : views::ViewsTestBase(std::unique_ptr<base::test::TaskEnvironment>(
+          std::make_unique<content::BrowserTaskEnvironment>(
+              content::BrowserTaskEnvironment::MainThreadType::UI,
+              content::BrowserTaskEnvironment::TimeSource::MOCK_TIME))) {}
 
 ChromeViewsTestBase::~ChromeViewsTestBase() = default;
 
diff --git a/chrome/test/views/chrome_views_test_base.h b/chrome/test/views/chrome_views_test_base.h
index 53430e7..a1adbae 100644
--- a/chrome/test/views/chrome_views_test_base.h
+++ b/chrome/test/views/chrome_views_test_base.h
@@ -5,46 +5,19 @@
 #ifndef CHROME_TEST_VIEWS_CHROME_VIEWS_TEST_BASE_H_
 #define CHROME_TEST_VIEWS_CHROME_VIEWS_TEST_BASE_H_
 
-#include <memory>
-
-#include "base/compiler_specific.h"
-#include "base/macros.h"
-#include "base/traits_bag.h"
-#include "content/public/test/browser_task_environment.h"
 #include "ui/views/test/views_test_base.h"
 
 // A base class for Chrome views unit tests. Changes the dependencies when they
 // need to be different than non-Chrome views.
 class ChromeViewsTestBase : public views::ViewsTestBase {
  public:
-  // Constructs a ChromeViewsTestBase with |traits| being forwarded to its
-  // BrowserTaskEnvironment. MainThreadType always defaults to UI and must not
-  // be specified. TimeSource defaults to MOCK_TIME but can be specified to
-  // override.
-  template <typename... TaskEnvironmentTraits>
-  NOINLINE explicit ChromeViewsTestBase(TaskEnvironmentTraits... traits)
-      : views::ViewsTestBase(
-            views::ViewsTestBase::SubclassManagesTaskEnvironment()),
-        task_environment_(
-            content::BrowserTaskEnvironment::MainThreadType::UI,
-            base::trait_helpers::GetEnum<
-                content::BrowserTaskEnvironment::TimeSource,
-                content::BrowserTaskEnvironment::TimeSource::MOCK_TIME>(
-                traits...),
-            base::trait_helpers::
-                Exclude<content::BrowserTaskEnvironment::TimeSource>::Filter(
-                    traits)...) {}
+  ChromeViewsTestBase();
+  ChromeViewsTestBase(const ChromeViewsTestBase&) = delete;
+  ChromeViewsTestBase& operator=(const ChromeViewsTestBase&) = delete;
   ~ChromeViewsTestBase() override;
 
   // views::ViewsTestBase:
   void SetUp() override;
-
- protected:
-  // Use this protected member directly to drive tasks posted within a
-  // ChromeViewsTestBase-based test.
-  content::BrowserTaskEnvironment task_environment_;
-
-  DISALLOW_COPY_AND_ASSIGN(ChromeViewsTestBase);
 };
 
 #endif  // CHROME_TEST_VIEWS_CHROME_VIEWS_TEST_BASE_H_
diff --git a/chromeos/BUILD.gn b/chromeos/BUILD.gn
index 8d1f075..ab111cb 100644
--- a/chromeos/BUILD.gn
+++ b/chromeos/BUILD.gn
@@ -264,13 +264,6 @@
   tast_test("chrome_all_tast_tests") {
     # To disable a specific test, add it the following list and cite a bug.
     tast_disabled_tests = [
-      # crbug.com/1000351
-      "crostini.Sanity.artifact",
-
-      # crbug.com/1006681
-      "crostini.GPUEnabledDownload",
-      "crostini.SanityDownload",
-
       # crbug.com/1051059
       "security.SELinuxProcessesARC",
 
@@ -278,31 +271,6 @@
       "security.NetworkListenersARC",
       "security.NetworkListenersNonARC",
 
-      # crbug.com/1054340
-      "crostini.AudioSanity.artifact",
-      "crostini.CopyPaste.wayland_to_wayland_artifact",
-      "crostini.CopyPaste.wayland_to_x11_artifact",
-      "crostini.DisplayDensity.wayland_artifact",
-      "crostini.DisplayDensity.x11_artifact",
-      "crostini.GPUEnabled.artifact_gpu",
-      "crostini.GPUEnabled.artifact_sw",
-      "crostini.LaunchTerminal.artifact",
-      "crostini.PackageInfo.artifact",
-      "crostini.PackageInstallUninstall.artifact",
-      "crostini.Restart.artifact",
-      "crostini.SecureCopyPaste.copy_wayland_artifact",
-      "crostini.SecureCopyPaste.copy_x11_artifact",
-      "crostini.SecureCopyPaste.paste_wayland_artifact",
-      "crostini.SecureCopyPaste.paste_x11_artifact",
-      "crostini.Toolkit.gtk3_wayland",
-      "crostini.Toolkit.gtk3_x11",
-      "crostini.Toolkit.qt5",
-      "crostini.Toolkit.tkinter",
-      "crostini.UninstallInvalidApp.artifact",
-      "crostini.VerifyAppWayland.artifact",
-      "crostini.VerifyAppX11.artifact",
-      "crostini.Webserver.artifact",
-
       # crbug.com/1056163
       "hwsec.LoginGuest",
     ]
diff --git a/chromeos/profiles/orderfile.newest.txt b/chromeos/profiles/orderfile.newest.txt
index 930c0b0..1c5525a1 100644
--- a/chromeos/profiles/orderfile.newest.txt
+++ b/chromeos/profiles/orderfile.newest.txt
@@ -1 +1 @@
-chromeos-chrome-orderfile-field-82-4072.0-1583746630-benchmark-82.0.4081.0-r1.orderfile.xz
\ No newline at end of file
+chromeos-chrome-orderfile-field-82-4085.6-1584957322-benchmark-82.0.4085.18-r1.orderfile.xz
\ No newline at end of file
diff --git a/components/arc/session/OWNERS b/components/arc/session/OWNERS
index 4c7e824d..868d420 100644
--- a/components/arc/session/OWNERS
+++ b/components/arc/session/OWNERS
@@ -1,3 +1,7 @@
+ereth@chromium.org
+lgcheng@google.com
+# backup reviewers
 hashimoto@chromium.org
 hidehiko@chromium.org
+jhorwich@chromium.org
 yusukes@chromium.org
diff --git a/components/autofill/content/browser/DEPS b/components/autofill/content/browser/DEPS
index 8891909..c98729d3 100644
--- a/components/autofill/content/browser/DEPS
+++ b/components/autofill/content/browser/DEPS
@@ -15,6 +15,6 @@
   ],
   '.*internal_authenticator_impl\.(cc|h)': [
     "+content/browser/webauth/authenticator_common.h",
-    "+third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h",
+    "+third_party/blink/public/mojom/webauthn/authenticator.mojom.h",
   ]
 }
diff --git a/components/autofill/content/browser/content_autofill_driver.cc b/components/autofill/content/browser/content_autofill_driver.cc
index df274c3..54174a0 100644
--- a/components/autofill/content/browser/content_autofill_driver.cc
+++ b/components/autofill/content/browser/content_autofill_driver.cc
@@ -108,15 +108,14 @@
   return render_frame_host_->GetRenderViewHost() != nullptr;
 }
 
-void ContentAutofillDriver::ConnectToAuthenticator(
-    mojo::PendingReceiver<blink::mojom::InternalAuthenticator> receiver) {
-#if defined(OS_ANDROID)
-  render_frame_host_->GetJavaInterfaces()->GetInterface(std::move(receiver));
-#else
-  authenticator_impl_ = std::make_unique<content::InternalAuthenticatorImpl>(
-      render_frame_host_, url::Origin::Create(payments::GetBaseSecureUrl()));
-  authenticator_impl_->Bind(std::move(receiver));
-#endif
+InternalAuthenticator*
+ContentAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() {
+  if (!authenticator_impl_) {
+    authenticator_impl_ =
+        autofill_manager_->client()->CreateCreditCardInternalAuthenticator(
+            render_frame_host_);
+  }
+  return authenticator_impl_.get();
 }
 
 void ContentAutofillDriver::SendFormDataToRenderer(
diff --git a/components/autofill/content/browser/content_autofill_driver.h b/components/autofill/content/browser/content_autofill_driver.h
index 9597492..1c90ca1 100644
--- a/components/autofill/content/browser/content_autofill_driver.h
+++ b/components/autofill/content/browser/content_autofill_driver.h
@@ -62,9 +62,7 @@
   ui::AXTreeID GetAxTreeId() const override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
-  void ConnectToAuthenticator(
-      mojo::PendingReceiver<blink::mojom::InternalAuthenticator> receiver)
-      override;
+  InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator() override;
   void SendFormDataToRenderer(int query_id,
                               RendererFormDataAction action,
                               const FormData& data) override;
@@ -165,10 +163,8 @@
   // a common root.
   AutofillManager* autofill_manager_;
 
-#if !defined(OS_ANDROID)
-  // Implementation of the InternalAuthenticator mojom.
-  std::unique_ptr<content::InternalAuthenticatorImpl> authenticator_impl_;
-#endif
+  // Pointer to an implementation of InternalAuthenticator.
+  std::unique_ptr<InternalAuthenticator> authenticator_impl_;
 
   // AutofillExternalDelegate instance that this object instantiates in the
   // case where the Autofill native UI is enabled.
diff --git a/components/autofill/content/browser/webauthn/BUILD.gn b/components/autofill/content/browser/webauthn/BUILD.gn
index a6068f9..bd8977e 100644
--- a/components/autofill/content/browser/webauthn/BUILD.gn
+++ b/components/autofill/content/browser/webauthn/BUILD.gn
@@ -13,6 +13,7 @@
     "internal_authenticator_impl.h",
   ]
   deps = [
+    "//components/autofill/core/browser",
     "//content/browser:for_internal_webauthn",
     "//content/public/browser",
   ]
diff --git a/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc b/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc
index b42f2801..e1291b98 100644
--- a/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc
+++ b/components/autofill/content/browser/webauthn/internal_authenticator_impl.cc
@@ -17,25 +17,21 @@
 namespace content {
 
 InternalAuthenticatorImpl::InternalAuthenticatorImpl(
-    RenderFrameHost* render_frame_host,
-    url::Origin effective_origin)
+    RenderFrameHost* render_frame_host)
     : InternalAuthenticatorImpl(render_frame_host,
-                                std::move(effective_origin),
                                 std::make_unique<AuthenticatorCommon>(
                                     render_frame_host,
                                     std::make_unique<base::OneShotTimer>())) {}
 
 InternalAuthenticatorImpl::InternalAuthenticatorImpl(
     RenderFrameHost* render_frame_host,
-    url::Origin effective_origin,
     std::unique_ptr<AuthenticatorCommon> authenticator_common)
     : WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)),
       render_frame_host_(render_frame_host),
-      effective_origin_(std::move(effective_origin)),
+      effective_origin_(render_frame_host->GetLastCommittedOrigin()),
       authenticator_common_(std::move(authenticator_common)) {
   DCHECK(render_frame_host_);
   DCHECK(authenticator_common_);
-  DCHECK(!effective_origin.opaque());
   // Disabling WebAuthn modal dialogs to avoid conflict with Autofill's own
   // modal dialogs. Since WebAuthn is designed for websites, rather than browser
   // components, the UI can be confusing for users in the case for Autofill.
@@ -50,41 +46,36 @@
   render_frame_host_->GetRoutingID();
 }
 
-void InternalAuthenticatorImpl::Bind(
-    mojo::PendingReceiver<blink::mojom::InternalAuthenticator> receiver) {
-  // If |render_frame_host_| is being unloaded then binding receivers are
-  // rejected.
-  if (!render_frame_host_->IsCurrent()) {
-    return;
-  }
-
-  DCHECK(!receiver_.is_bound());
-  receiver_.Bind(std::move(receiver));
+void InternalAuthenticatorImpl::SetEffectiveOrigin(const url::Origin& origin) {
+  effective_origin_ = url::Origin(origin);
+  DCHECK(!effective_origin_.opaque());
 }
 
-// mojom::InternalAuthenticator
 void InternalAuthenticatorImpl::MakeCredential(
     blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
-    MakeCredentialCallback callback) {
+    blink::mojom::Authenticator::MakeCredentialCallback callback) {
   authenticator_common_->MakeCredential(effective_origin_, std::move(options),
                                         std::move(callback));
 }
 
-// mojom::InternalAuthenticator
 void InternalAuthenticatorImpl::GetAssertion(
     blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
-    GetAssertionCallback callback) {
+    blink::mojom::Authenticator::GetAssertionCallback callback) {
   authenticator_common_->GetAssertion(effective_origin_, std::move(options),
                                       std::move(callback));
 }
 
-// mojom::InternalAuthenticator
 void InternalAuthenticatorImpl::IsUserVerifyingPlatformAuthenticatorAvailable(
-    IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
+    blink::mojom::Authenticator::
+        IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
   authenticator_common_->IsUserVerifyingPlatformAuthenticatorAvailable(
       std::move(callback));
 }
 
+void InternalAuthenticatorImpl::Cancel() {
+  authenticator_common_->Cancel();
+}
+
 void InternalAuthenticatorImpl::DidFinishNavigation(
     NavigationHandle* navigation_handle) {
   // If the RenderFrameHost itself is navigated then this function will cause
@@ -98,7 +89,6 @@
     return;
   }
 
-  receiver_.reset();
   authenticator_common_->Cleanup();
 }
 
diff --git a/components/autofill/content/browser/webauthn/internal_authenticator_impl.h b/components/autofill/content/browser/webauthn/internal_authenticator_impl.h
index 022a17ac..e80da274 100644
--- a/components/autofill/content/browser/webauthn/internal_authenticator_impl.h
+++ b/components/autofill/content/browser/webauthn/internal_authenticator_impl.h
@@ -11,11 +11,13 @@
 #include <string>
 
 #include "base/macros.h"
+#include "components/autofill/core/browser/payments/internal_authenticator.h"
 #include "content/browser/webauth/authenticator_common.h"
 #include "content/public/browser/web_contents_observer.h"
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "mojo/public/cpp/bindings/receiver.h"
-#include "third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
+#include "url/origin.h"
 
 namespace url {
 class Origin;
@@ -28,21 +30,29 @@
 // Implementation of the public InternalAuthenticator interface.
 // This class is meant only for trusted and internal components of Chrome to
 // use.
-class InternalAuthenticatorImpl : public blink::mojom::InternalAuthenticator,
+class InternalAuthenticatorImpl : public autofill::InternalAuthenticator,
                                   public WebContentsObserver {
  public:
-  InternalAuthenticatorImpl(RenderFrameHost* render_frame_host,
-                            url::Origin effective_origin);
+  explicit InternalAuthenticatorImpl(RenderFrameHost* render_frame_host);
 
   ~InternalAuthenticatorImpl() override;
 
-  // Creates a binding between this implementation and |receiver|.
-  //
-  // Note that one InternalAuthenticatorImpl instance can be bound to
-  // exactly one interface connection at a time, and disconnected when the frame
-  // navigates to a new active document.
-  void Bind(
-      mojo::PendingReceiver<blink::mojom::InternalAuthenticator> receiver);
+  // InternalAuthenticator:
+  void SetEffectiveOrigin(const url::Origin& origin) override;
+  void MakeCredential(
+      blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
+      blink::mojom::Authenticator::MakeCredentialCallback callback) override;
+  void GetAssertion(
+      blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
+      blink::mojom::Authenticator::GetAssertionCallback callback) override;
+  void IsUserVerifyingPlatformAuthenticatorAvailable(
+      blink::mojom::Authenticator::
+          IsUserVerifyingPlatformAuthenticatorAvailableCallback callback)
+      override;
+  void Cancel() override;
+
+  // WebContentsObserver:
+  void DidFinishNavigation(NavigationHandle* navigation_handle) override;
 
  private:
   friend class InternalAuthenticatorImplTest;
@@ -53,32 +63,16 @@
   // tests.
   InternalAuthenticatorImpl(
       RenderFrameHost* render_frame_host,
-      url::Origin effective_origin,
       std::unique_ptr<AuthenticatorCommon> authenticator_common);
 
   AuthenticatorCommon* get_authenticator_common_for_testing() {
     return authenticator_common_.get();
   }
 
-  // mojom::InternalAuthenticator
-  void MakeCredential(
-      blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
-      MakeCredentialCallback callback) override;
-  void GetAssertion(blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
-                    GetAssertionCallback callback) override;
-  void IsUserVerifyingPlatformAuthenticatorAvailable(
-      IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) override;
-
-  // WebContentsObserver
-  void DidFinishNavigation(NavigationHandle* navigation_handle) override;
-
   RenderFrameHost* const render_frame_host_;
-  const url::Origin effective_origin_;
+  url::Origin effective_origin_;
   std::unique_ptr<AuthenticatorCommon> authenticator_common_;
 
-  // Owns pipes to this Authenticator from |render_frame_host_|.
-  mojo::Receiver<blink::mojom::InternalAuthenticator> receiver_{this};
-
   base::WeakPtrFactory<InternalAuthenticatorImpl> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(InternalAuthenticatorImpl);
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn
index a6c4c3d..495adc18 100644
--- a/components/autofill/core/browser/BUILD.gn
+++ b/components/autofill/core/browser/BUILD.gn
@@ -325,6 +325,7 @@
       "payments/credit_card_fido_authenticator.h",
       "payments/fido_authentication_strike_database.cc",
       "payments/fido_authentication_strike_database.h",
+      "payments/internal_authenticator.h",
     ]
   }
 
@@ -505,6 +506,7 @@
     sources += [
       "payments/test_credit_card_fido_authenticator.cc",
       "payments/test_credit_card_fido_authenticator.h",
+      "payments/test_internal_authenticator.h",
     ]
 
     deps += [ "//third_party/blink/public/common" ]
diff --git a/components/autofill/core/browser/DEPS b/components/autofill/core/browser/DEPS
index b2ccc4b..cab8e0c 100644
--- a/components/autofill/core/browser/DEPS
+++ b/components/autofill/core/browser/DEPS
@@ -44,7 +44,10 @@
   "(test_)?credit_card_fido_authenticator\.(cc|h)": [
     "+device/fido/fido_constants.h",
     "+third_party/blink/public/mojom/webauthn/authenticator.mojom.h",
-    "+third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h",
+  ],
+  "(test_)?internal_authenticator\.h": [
+    "+device/fido/fido_constants.h",
+    "+third_party/blink/public/mojom/webauthn/authenticator.mojom.h",
   ],
   "autofill_driver\.h": [
     "+third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
diff --git a/components/autofill/core/browser/autofill_client.cc b/components/autofill/core/browser/autofill_client.cc
index 9cf112d..3e56a59c 100644
--- a/components/autofill/core/browser/autofill_client.cc
+++ b/components/autofill/core/browser/autofill_client.cc
@@ -20,6 +20,14 @@
   return std::string();
 }
 
+#if !defined(OS_IOS)
+std::unique_ptr<InternalAuthenticator>
+AutofillClient::CreateCreditCardInternalAuthenticator(
+    content::RenderFrameHost* rfh) {
+  return nullptr;
+}
+#endif
+
 LogManager* AutofillClient::GetLogManager() const {
   return nullptr;
 }
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h
index ffa9105..e445a82 100644
--- a/components/autofill/core/browser/autofill_client.h
+++ b/components/autofill/core/browser/autofill_client.h
@@ -26,6 +26,10 @@
 #include "ui/base/window_open_disposition.h"
 #include "url/gurl.h"
 
+#if !defined(OS_IOS)
+#include "components/autofill/core/browser/payments/internal_authenticator.h"
+#endif
+
 class PrefService;
 
 namespace content {
@@ -265,6 +269,14 @@
   virtual std::vector<std::string> GetBinRangeWhitelistForVirtualCards() = 0;
 #endif
 
+#if !defined(OS_IOS)
+  // Creates the appropriate implementation of InternalAuthenticator. May be
+  // null for platforms that don't support this, in which case standard CVC
+  // authentication will be used instead.
+  virtual std::unique_ptr<InternalAuthenticator>
+  CreateCreditCardInternalAuthenticator(content::RenderFrameHost* rfh);
+#endif
+
   // Causes the Autofill settings UI to be shown. If |show_credit_card_settings|
   // is true, will show the credit card specific subpage.
   virtual void ShowAutofillSettings(bool show_credit_card_settings) = 0;
diff --git a/components/autofill/core/browser/autofill_driver.h b/components/autofill/core/browser/autofill_driver.h
index 641f0a7..891201df 100644
--- a/components/autofill/core/browser/autofill_driver.h
+++ b/components/autofill/core/browser/autofill_driver.h
@@ -14,7 +14,7 @@
 #include "net/base/network_isolation_key.h"
 
 #if !defined(OS_IOS)
-#include "third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
+#include "components/autofill/core/browser/payments/internal_authenticator.h"
 #endif
 
 namespace network {
@@ -69,9 +69,9 @@
   virtual bool RendererIsAvailable() = 0;
 
 #if !defined(OS_IOS)
-  // Binds the mojom request in order to facilitate WebAuthn flows.
-  virtual void ConnectToAuthenticator(
-      mojo::PendingReceiver<blink::mojom::InternalAuthenticator> receiver) = 0;
+  // Gets or creates a pointer to an implementation of InternalAuthenticator.
+  virtual InternalAuthenticator*
+  GetOrCreateCreditCardInternalAuthenticator() = 0;
 #endif
 
   // Forwards |data| to the renderer. |query_id| is the id of the renderer's
diff --git a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
index 3c9a8b68..0aeceae 100644
--- a/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_access_manager_unittest.cc
@@ -72,6 +72,7 @@
 #if !defined(OS_IOS)
 #include "components/autofill/core/browser/payments/fido_authentication_strike_database.h"
 #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
+#include "components/autofill/core/browser/payments/test_internal_authenticator.h"
 #endif
 
 using base::ASCIIToUTF16;
@@ -183,6 +184,7 @@
         autofill_manager_->credit_card_access_manager();
 
 #if !defined(OS_IOS)
+    autofill_driver_->SetAuthenticator(new TestInternalAuthenticator());
     credit_card_access_manager_->set_fido_authenticator_for_testing(
         std::make_unique<TestCreditCardFIDOAuthenticator>(
             autofill_driver_.get(), &autofill_client_));
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
index 8d44ff2..d1eb0697 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.cc
@@ -18,13 +18,14 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/fido_authentication_strike_database.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
+#include "components/autofill/core/browser/payments/payments_service_url.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/common/autofill_payments_features.h"
 #include "components/autofill/core/common/autofill_prefs.h"
 #include "components/signin/public/identity_manager/account_info.h"
 #include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
-#include "third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
 #include "url/gurl.h"
+#include "url/origin.h"
 
 namespace autofill {
 
@@ -124,11 +125,7 @@
     base::OnceCallback<void(bool)> callback) {
   if (base::FeatureList::IsEnabled(
           features::kAutofillCreditCardAuthentication)) {
-    if (!authenticator_.is_bound()) {
-      autofill_driver_->ConnectToAuthenticator(
-          authenticator_.BindNewPipeAndPassReceiver());
-    }
-    authenticator_->IsUserVerifyingPlatformAuthenticatorAvailable(
+    authenticator()->IsUserVerifyingPlatformAuthenticatorAvailable(
         std::move(callback));
   } else {
     std::move(callback).Run(false);
@@ -247,10 +244,6 @@
 
 void CreditCardFIDOAuthenticator::GetAssertion(
     PublicKeyCredentialRequestOptionsPtr request_options) {
-  if (!authenticator_.is_bound()) {
-    autofill_driver_->ConnectToAuthenticator(
-        authenticator_.BindNewPipeAndPassReceiver());
-  }
 #if !defined(OS_ANDROID)
   // On desktop, during an opt-in flow, close the WebAuthn offer dialog and get
   // ready to show the OS level authentication dialog. If dialog is already
@@ -269,7 +262,7 @@
     }
   }
 #endif
-  authenticator_->GetAssertion(
+  authenticator()->GetAssertion(
       std::move(request_options),
       base::BindOnce(&CreditCardFIDOAuthenticator::OnDidGetAssertion,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -277,10 +270,6 @@
 
 void CreditCardFIDOAuthenticator::MakeCredential(
     PublicKeyCredentialCreationOptionsPtr creation_options) {
-  if (!authenticator_.is_bound()) {
-    autofill_driver_->ConnectToAuthenticator(
-        authenticator_.BindNewPipeAndPassReceiver());
-  }
 #if !defined(OS_ANDROID)
   // On desktop, close the WebAuthn offer dialog and get ready to show the OS
   // level authentication dialog. If dialog is already closed, then the offer
@@ -297,7 +286,7 @@
     return;
   }
 #endif
-  authenticator_->MakeCredential(
+  authenticator()->MakeCredential(
       std::move(creation_options),
       base::BindOnce(&CreditCardFIDOAuthenticator::OnDidMakeCredential,
                      weak_ptr_factory_.GetWeakPtr()));
@@ -761,4 +750,14 @@
   ::autofill::prefs::SetCreditCardFIDOAuthEnabled(autofill_client_->GetPrefs(),
                                                   user_is_opted_in_);
 }
+
+InternalAuthenticator* CreditCardFIDOAuthenticator::authenticator() {
+  if (!authenticator_) {
+    authenticator_ =
+        autofill_driver_->GetOrCreateCreditCardInternalAuthenticator();
+    authenticator()->SetEffectiveOrigin(
+        url::Origin::Create(payments::GetBaseSecureUrl()));
+  }
+  return authenticator_;
+}
 }  // namespace autofill
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
index d6493334..93b1135 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator.h
@@ -14,17 +14,17 @@
 #include "components/autofill/core/browser/data_model/credit_card.h"
 #include "components/autofill/core/browser/payments/fido_authentication_strike_database.h"
 #include "components/autofill/core/browser/payments/full_card_request.h"
+#include "components/autofill/core/browser/payments/internal_authenticator.h"
 #include "components/autofill/core/browser/payments/payments_client.h"
 #include "device/fido/fido_constants.h"
 #include "mojo/public/cpp/bindings/remote.h"
-#include "third_party/blink/public/mojom/webauthn/internal_authenticator.mojom.h"
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
 
 namespace autofill {
 
 using blink::mojom::AuthenticatorStatus;
 using blink::mojom::GetAssertionAuthenticatorResponse;
 using blink::mojom::GetAssertionAuthenticatorResponsePtr;
-using blink::mojom::InternalAuthenticator;
 using blink::mojom::MakeCredentialAuthenticatorResponse;
 using blink::mojom::MakeCredentialAuthenticatorResponsePtr;
 using blink::mojom::PublicKeyCredentialCreationOptions;
@@ -230,6 +230,9 @@
   // Updates the user preference to the value of |user_is_opted_in_|.
   void UpdateUserPref();
 
+  // Gets or creates Authenticator pointer to facilitate WebAuthn.
+  InternalAuthenticator* authenticator();
+
   // Card being unmasked.
   const CreditCard* card_;
 
@@ -253,7 +256,7 @@
   payments::PaymentsClient* const payments_client_;
 
   // Authenticator pointer to facilitate WebAuthn.
-  mojo::Remote<InternalAuthenticator> authenticator_;
+  InternalAuthenticator* authenticator_ = nullptr;
 
   // Responsible for getting the full card details, including the PAN and the
   // CVC.
diff --git a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
index 29f2ff6..abe233c 100644
--- a/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
+++ b/components/autofill/core/browser/payments/credit_card_fido_authenticator_unittest.cc
@@ -40,6 +40,7 @@
 #include "components/autofill/core/browser/metrics/form_events.h"
 #include "components/autofill/core/browser/payments/test_authentication_requester.h"
 #include "components/autofill/core/browser/payments/test_credit_card_fido_authenticator.h"
+#include "components/autofill/core/browser/payments/test_internal_authenticator.h"
 #include "components/autofill/core/browser/payments/test_payments_client.h"
 #include "components/autofill/core/browser/personal_data_manager.h"
 #include "components/autofill/core/browser/test_autofill_client.h"
@@ -128,6 +129,7 @@
     requester_.reset(new TestAuthenticationRequester());
     autofill_driver_ =
         std::make_unique<testing::NiceMock<TestAutofillDriver>>();
+    autofill_driver_->SetAuthenticator(new TestInternalAuthenticator());
 
     payments::TestPaymentsClient* payments_client =
         new payments::TestPaymentsClient(
diff --git a/components/autofill/core/browser/payments/internal_authenticator.h b/components/autofill/core/browser/payments/internal_authenticator.h
new file mode 100644
index 0000000..0d3007be
--- /dev/null
+++ b/components/autofill/core/browser/payments/internal_authenticator.h
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_INTERNAL_AUTHENTICATOR_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_INTERNAL_AUTHENTICATOR_H_
+
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
+#include "url/origin.h"
+
+namespace autofill {
+
+// Interface similar to blink::mojom::Authenticator meant only for internal
+// components in Chrome to use in order to direct authenticators to create or
+// use a public key credential. Unlike Authenticator, the caller will be
+// allowed to set its own effective origin.
+class InternalAuthenticator {
+ public:
+  virtual ~InternalAuthenticator() = default;
+
+  // Sets the effective origin of the caller. Since this may be a browser
+  // process, the Relying Party ID may be different from the renderer's origin.
+  virtual void SetEffectiveOrigin(const url::Origin& origin) = 0;
+
+  // Gets the credential info for a new public key credential created by an
+  // authenticator for the given |options|. Invokes |callback| with credentials
+  // if authentication was successful.
+  virtual void MakeCredential(
+      blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
+      blink::mojom::Authenticator::MakeCredentialCallback callback) = 0;
+
+  // Uses an existing credential to produce an assertion for the given
+  // |options|. Invokes |callback| with assertion response if authentication
+  // was successful.
+  virtual void GetAssertion(
+      blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
+      blink::mojom::Authenticator::GetAssertionCallback callback) = 0;
+
+  // Returns true if the user platform provides an authenticator. Relying
+  // Parties use this method to determine whether they can create a new
+  // credential using a user-verifying platform authenticator.
+  virtual void IsUserVerifyingPlatformAuthenticatorAvailable(
+      blink::mojom::Authenticator::
+          IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) = 0;
+
+  // Cancel an ongoing MakeCredential or GetAssertion request.
+  // Only one MakeCredential or GetAssertion call at a time is allowed,
+  // any future calls are cancelled.
+  virtual void Cancel() = 0;
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_INTERNAL_AUTHENTICATOR_H_
diff --git a/components/autofill/core/browser/payments/test_internal_authenticator.h b/components/autofill/core/browser/payments/test_internal_authenticator.h
new file mode 100644
index 0000000..ec6ec89
--- /dev/null
+++ b/components/autofill/core/browser/payments/test_internal_authenticator.h
@@ -0,0 +1,37 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_INTERNAL_AUTHENTICATOR_H_
+#define COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_INTERNAL_AUTHENTICATOR_H_
+
+#include "components/autofill/core/browser/payments/internal_authenticator.h"
+#include "third_party/blink/public/mojom/webauthn/authenticator.mojom.h"
+#include "url/origin.h"
+
+namespace autofill {
+
+// Subclass of InternalAuthenticator meant for testing purposes.
+class TestInternalAuthenticator : public InternalAuthenticator {
+ public:
+  TestInternalAuthenticator() = default;
+  ~TestInternalAuthenticator() override = default;
+
+  // InternalAuthenticator:
+  void SetEffectiveOrigin(const url::Origin& origin) override {}
+  void MakeCredential(
+      blink::mojom::PublicKeyCredentialCreationOptionsPtr options,
+      blink::mojom::Authenticator::MakeCredentialCallback callback) override {}
+  void GetAssertion(
+      blink::mojom::PublicKeyCredentialRequestOptionsPtr options,
+      blink::mojom::Authenticator::GetAssertionCallback callback) override {}
+  void IsUserVerifyingPlatformAuthenticatorAvailable(
+      blink::mojom::Authenticator::
+          IsUserVerifyingPlatformAuthenticatorAvailableCallback callback)
+      override {}
+  void Cancel() override {}
+};
+
+}  // namespace autofill
+
+#endif  // COMPONENTS_AUTOFILL_CORE_BROWSER_PAYMENTS_TEST_INTERNAL_AUTHENTICATOR_H_
diff --git a/components/autofill/core/browser/test_autofill_driver.cc b/components/autofill/core/browser/test_autofill_driver.cc
index 0c5348dd..dca5955 100644
--- a/components/autofill/core/browser/test_autofill_driver.cc
+++ b/components/autofill/core/browser/test_autofill_driver.cc
@@ -48,8 +48,10 @@
 }
 
 #if !defined(OS_IOS)
-void TestAutofillDriver::ConnectToAuthenticator(
-    mojo::PendingReceiver<blink::mojom::InternalAuthenticator> receiver) {}
+InternalAuthenticator*
+TestAutofillDriver::GetOrCreateCreditCardInternalAuthenticator() {
+  return test_authenticator_.get();
+}
 #endif
 
 void TestAutofillDriver::SendFormDataToRenderer(int query_id,
@@ -118,4 +120,11 @@
   test_shared_loader_factory_ = url_loader_factory;
 }
 
+#if !defined(OS_IOS)
+void TestAutofillDriver::SetAuthenticator(
+    InternalAuthenticator* authenticator_) {
+  test_authenticator_.reset(authenticator_);
+}
+#endif
+
 }  // namespace autofill
diff --git a/components/autofill/core/browser/test_autofill_driver.h b/components/autofill/core/browser/test_autofill_driver.h
index aff0ea1..14dbce46 100644
--- a/components/autofill/core/browser/test_autofill_driver.h
+++ b/components/autofill/core/browser/test_autofill_driver.h
@@ -13,6 +13,10 @@
 #include "mojo/public/cpp/bindings/pending_receiver.h"
 #include "services/network/test/test_url_loader_factory.h"
 
+#if !defined(OS_IOS)
+#include "components/autofill/core/browser/payments/internal_authenticator.h"
+#endif
+
 namespace autofill {
 
 // This class is only for easier writing of tests.
@@ -29,9 +33,7 @@
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
   bool RendererIsAvailable() override;
 #if !defined(OS_IOS)
-  void ConnectToAuthenticator(
-      mojo::PendingReceiver<blink::mojom::InternalAuthenticator> receiver)
-      override;
+  InternalAuthenticator* GetOrCreateCreditCardInternalAuthenticator() override;
 #endif
   void SendFormDataToRenderer(int query_id,
                               RendererFormDataAction action,
@@ -65,6 +67,9 @@
 
   void SetSharedURLLoaderFactory(
       scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
+#if !defined(OS_IOS)
+  void SetAuthenticator(InternalAuthenticator* authenticator_);
+#endif
 
  private:
   network::TestURLLoaderFactory test_url_loader_factory_;
@@ -73,6 +78,10 @@
   bool is_in_main_frame_ = false;
   net::NetworkIsolationKey network_isolation_key_;
 
+#if !defined(OS_IOS)
+  std::unique_ptr<InternalAuthenticator> test_authenticator_;
+#endif
+
   DISALLOW_COPY_AND_ASSIGN(TestAutofillDriver);
 };
 
diff --git a/components/browser_ui/settings/DEPS b/components/browser_ui/settings/DEPS
new file mode 100644
index 0000000..d21e072
--- /dev/null
+++ b/components/browser_ui/settings/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+  "+ui/android",
+]
diff --git a/components/browser_ui/settings/OWNERS b/components/browser_ui/settings/OWNERS
new file mode 100644
index 0000000..cf49cb9
--- /dev/null
+++ b/components/browser_ui/settings/OWNERS
@@ -0,0 +1 @@
+file://chrome/android/java/src/org/chromium/chrome/browser/settings/OWNERS
\ No newline at end of file
diff --git a/components/browser_ui/settings/README.md b/components/browser_ui/settings/README.md
new file mode 100644
index 0000000..9a90da49
--- /dev/null
+++ b/components/browser_ui/settings/README.md
@@ -0,0 +1,22 @@
+# Chromium for Android Settings
+
+## Getting Started
+
+The Android developer [Settings
+guide](https://developer.android.com/guide/topics/ui/settings) is the best place
+to start before contributing to Chromium for Android's settings.
+
+## Helper Classes
+
+Many common utility functions that are useful for developing settings screens in
+Chromium for Android can be found in `//components/browser_ui/settings/android`.
+
+## Widgets
+
+The `widget` subdirectory contains a number of extensions of AndroidX
+[Preference](https://developer.android.com/reference/androidx/preference/Preference)
+classes that provide Chromium-specific behavior (like Managed preferences) or
+common Chromium UI components (like buttons).
+
+The base Preference classes included in the AndroidX Preference library can also
+be used directly in Chromium for Android Settings screens.
diff --git a/components/browser_ui/settings/android/BUILD.gn b/components/browser_ui/settings/android/BUILD.gn
new file mode 100644
index 0000000..18b7fd4
--- /dev/null
+++ b/components/browser_ui/settings/android/BUILD.gn
@@ -0,0 +1,69 @@
+# 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("//build/config/android/rules.gni")
+
+android_library("java") {
+  sources = [
+    "java/src/org/chromium/components/browser_ui/settings/ManagedPreferenceDelegate.java",
+    "java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java",
+    "java/src/org/chromium/components/browser_ui/settings/SearchUtils.java",
+    "java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/ButtonPreference.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/ChromeBaseCheckBoxPreference.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreference.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java",
+    "widget/java/src/org/chromium/components/browser_ui/settings/TextMessagePreference.java",
+  ]
+  deps = [
+    ":java_resources",
+    "//base:base_java",
+    "//third_party/android_deps:android_support_v7_appcompat_java",
+    "//third_party/android_deps:androidx_annotation_annotation_java",
+    "//third_party/android_deps:androidx_preference_preference_java",
+
+    # TODO(crbug.com/1017190): Remove the following deps once we stop linting individual targets.
+    "//components/browser_ui/styles/android:java_resources",
+    "//third_party/android_deps:com_google_android_material_material_java",
+    "//ui/android:ui_java",
+  ]
+}
+
+android_resources("java_resources") {
+  deps = [
+    "//components/browser_ui/strings/android:browser_ui_strings_grd",
+    "//components/browser_ui/styles/android:java_resources",
+    "//third_party/android_deps:androidx_preference_preference_java",
+    "//third_party/android_deps:com_google_android_material_material_java",
+    "//ui/android:ui_java_resources",
+  ]
+  sources = [
+    "java/res/drawable-hdpi/controlled_setting_mandatory.png",
+    "java/res/drawable-hdpi/ic_account_child_grey600_36dp.png",
+    "java/res/drawable-mdpi/controlled_setting_mandatory.png",
+    "java/res/drawable-mdpi/ic_account_child_grey600_36dp.png",
+    "java/res/drawable-xhdpi/controlled_setting_mandatory.png",
+    "java/res/drawable-xhdpi/ic_account_child_grey600_36dp.png",
+    "java/res/drawable-xxhdpi/controlled_setting_mandatory.png",
+    "java/res/drawable-xxhdpi/ic_account_child_grey600_36dp.png",
+    "java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png",
+    "java/res/layout/button_preference_button.xml",
+    "java/res/layout/button_preference_layout.xml",
+    "java/res/layout/checkable_image_view_widget.xml",
+    "java/res/layout/preference_chrome_image_view.xml",
+    "java/res/layout/preference_compat.xml",
+    "java/res/layout/preference_spinner.xml",
+    "java/res/layout/preference_spinner_single_line.xml",
+    "java/res/layout/preference_spinner_single_line_item.xml",
+    "java/res/values-sw720dp-v17/styles.xml",
+    "java/res/values-sw720dp-v21/styles.xml",
+    "java/res/values-v21/styles.xml",
+    "java/res/values/attrs.xml",
+    "java/res/values/styles.xml",
+  ]
+  custom_package = "org.chromium.components.browser_ui.settings"
+}
diff --git a/chrome/browser/settings/android/java/res/drawable-hdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-hdpi/controlled_setting_mandatory.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-hdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-hdpi/controlled_setting_mandatory.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-hdpi/ic_account_child_grey600_36dp.png b/components/browser_ui/settings/android/java/res/drawable-hdpi/ic_account_child_grey600_36dp.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-hdpi/ic_account_child_grey600_36dp.png
rename to components/browser_ui/settings/android/java/res/drawable-hdpi/ic_account_child_grey600_36dp.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-mdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-mdpi/controlled_setting_mandatory.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-mdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-mdpi/controlled_setting_mandatory.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-mdpi/ic_account_child_grey600_36dp.png b/components/browser_ui/settings/android/java/res/drawable-mdpi/ic_account_child_grey600_36dp.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-mdpi/ic_account_child_grey600_36dp.png
rename to components/browser_ui/settings/android/java/res/drawable-mdpi/ic_account_child_grey600_36dp.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-xhdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-xhdpi/controlled_setting_mandatory.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-xhdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-xhdpi/controlled_setting_mandatory.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-xhdpi/ic_account_child_grey600_36dp.png b/components/browser_ui/settings/android/java/res/drawable-xhdpi/ic_account_child_grey600_36dp.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-xhdpi/ic_account_child_grey600_36dp.png
rename to components/browser_ui/settings/android/java/res/drawable-xhdpi/ic_account_child_grey600_36dp.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-xxhdpi/controlled_setting_mandatory.png b/components/browser_ui/settings/android/java/res/drawable-xxhdpi/controlled_setting_mandatory.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-xxhdpi/controlled_setting_mandatory.png
rename to components/browser_ui/settings/android/java/res/drawable-xxhdpi/controlled_setting_mandatory.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-xxhdpi/ic_account_child_grey600_36dp.png b/components/browser_ui/settings/android/java/res/drawable-xxhdpi/ic_account_child_grey600_36dp.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-xxhdpi/ic_account_child_grey600_36dp.png
rename to components/browser_ui/settings/android/java/res/drawable-xxhdpi/ic_account_child_grey600_36dp.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png b/components/browser_ui/settings/android/java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png
similarity index 100%
rename from chrome/browser/settings/android/java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png
rename to components/browser_ui/settings/android/java/res/drawable-xxxhdpi/ic_account_child_grey600_36dp.png
Binary files differ
diff --git a/chrome/browser/settings/android/java/res/layout/button_preference_button.xml b/components/browser_ui/settings/android/java/res/layout/button_preference_button.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/layout/button_preference_button.xml
rename to components/browser_ui/settings/android/java/res/layout/button_preference_button.xml
diff --git a/chrome/browser/settings/android/java/res/layout/button_preference_layout.xml b/components/browser_ui/settings/android/java/res/layout/button_preference_layout.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/layout/button_preference_layout.xml
rename to components/browser_ui/settings/android/java/res/layout/button_preference_layout.xml
diff --git a/chrome/browser/settings/android/java/res/layout/checkable_image_view_widget.xml b/components/browser_ui/settings/android/java/res/layout/checkable_image_view_widget.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/layout/checkable_image_view_widget.xml
rename to components/browser_ui/settings/android/java/res/layout/checkable_image_view_widget.xml
diff --git a/chrome/browser/settings/android/java/res/layout/preference_chrome_image_view.xml b/components/browser_ui/settings/android/java/res/layout/preference_chrome_image_view.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/layout/preference_chrome_image_view.xml
rename to components/browser_ui/settings/android/java/res/layout/preference_chrome_image_view.xml
diff --git a/chrome/browser/settings/android/java/res/layout/preference_compat.xml b/components/browser_ui/settings/android/java/res/layout/preference_compat.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/layout/preference_compat.xml
rename to components/browser_ui/settings/android/java/res/layout/preference_compat.xml
diff --git a/chrome/browser/settings/android/java/res/layout/preference_spinner.xml b/components/browser_ui/settings/android/java/res/layout/preference_spinner.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/layout/preference_spinner.xml
rename to components/browser_ui/settings/android/java/res/layout/preference_spinner.xml
diff --git a/chrome/browser/settings/android/java/res/layout/preference_spinner_single_line.xml b/components/browser_ui/settings/android/java/res/layout/preference_spinner_single_line.xml
similarity index 93%
rename from chrome/browser/settings/android/java/res/layout/preference_spinner_single_line.xml
rename to components/browser_ui/settings/android/java/res/layout/preference_spinner_single_line.xml
index cd628c02..d6101b17 100644
--- a/chrome/browser/settings/android/java/res/layout/preference_spinner_single_line.xml
+++ b/components/browser_ui/settings/android/java/res/layout/preference_spinner_single_line.xml
@@ -24,7 +24,7 @@
         android:id="@+id/spinner"
         android:layout_height="wrap_content"
         android:layout_width="match_parent"
-        android:textSize="16sp"
+        android:textAppearance="@style/TextAppearance.TextLarge.Primary"
         android:paddingTop="6dp" />
 
 </LinearLayout>
diff --git a/chrome/browser/settings/android/java/res/layout/preference_spinner_single_line_item.xml b/components/browser_ui/settings/android/java/res/layout/preference_spinner_single_line_item.xml
similarity index 89%
rename from chrome/browser/settings/android/java/res/layout/preference_spinner_single_line_item.xml
rename to components/browser_ui/settings/android/java/res/layout/preference_spinner_single_line_item.xml
index 5f7db37..481f1b4 100644
--- a/chrome/browser/settings/android/java/res/layout/preference_spinner_single_line_item.xml
+++ b/components/browser_ui/settings/android/java/res/layout/preference_spinner_single_line_item.xml
@@ -9,7 +9,7 @@
     android:id="@android:id/text1"
     style="?android:attr/spinnerItemStyle"
     android:maxLines="1"
-    android:textSize="16sp"
+    android:textAppearance="@style/TextAppearance.TextLarge.Primary"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:ellipsize="marquee"
diff --git a/chrome/browser/settings/android/java/res/values-sw720dp-v17/styles.xml b/components/browser_ui/settings/android/java/res/values-sw720dp-v17/styles.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/values-sw720dp-v17/styles.xml
rename to components/browser_ui/settings/android/java/res/values-sw720dp-v17/styles.xml
diff --git a/chrome/browser/settings/android/java/res/values-sw720dp-v21/styles.xml b/components/browser_ui/settings/android/java/res/values-sw720dp-v21/styles.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/values-sw720dp-v21/styles.xml
rename to components/browser_ui/settings/android/java/res/values-sw720dp-v21/styles.xml
diff --git a/chrome/browser/settings/android/java/res/values-v21/styles.xml b/components/browser_ui/settings/android/java/res/values-v21/styles.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/values-v21/styles.xml
rename to components/browser_ui/settings/android/java/res/values-v21/styles.xml
diff --git a/chrome/browser/settings/android/java/res/values/attrs.xml b/components/browser_ui/settings/android/java/res/values/attrs.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/values/attrs.xml
rename to components/browser_ui/settings/android/java/res/values/attrs.xml
diff --git a/chrome/browser/settings/android/java/res/values/styles.xml b/components/browser_ui/settings/android/java/res/values/styles.xml
similarity index 100%
rename from chrome/browser/settings/android/java/res/values/styles.xml
rename to components/browser_ui/settings/android/java/res/values/styles.xml
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferenceDelegate.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferenceDelegate.java
similarity index 97%
rename from chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferenceDelegate.java
rename to components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferenceDelegate.java
index c3f7a39..709aa9c 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferenceDelegate.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferenceDelegate.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import androidx.preference.Preference;
 
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtils.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
similarity index 99%
rename from chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtils.java
rename to components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
index 154b52f..44d99827 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/ManagedPreferencesUtils.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/ManagedPreferencesUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SearchUtils.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SearchUtils.java
similarity index 98%
rename from chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SearchUtils.java
rename to components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SearchUtils.java
index 4fe29e16..4739629 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SearchUtils.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SearchUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.app.Activity;
 import android.view.MenuItem;
diff --git a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SettingsUtils.java b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java
similarity index 98%
rename from chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SettingsUtils.java
rename to components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java
index dbec26e..b95a86c6 100644
--- a/chrome/browser/settings/android/java/src/org/chromium/chrome/browser/settings/SettingsUtils.java
+++ b/components/browser_ui/settings/android/java/src/org/chromium/components/browser_ui/settings/SettingsUtils.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.app.Activity;
 import android.content.Context;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ButtonPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ButtonPreference.java
similarity index 95%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ButtonPreference.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ButtonPreference.java
index 89ee74d1..4d7bbd4 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ButtonPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ButtonPreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBaseCheckBoxPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBaseCheckBoxPreference.java
similarity index 96%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBaseCheckBoxPreference.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBaseCheckBoxPreference.java
index 1c145ee..6ee617c3 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBaseCheckBoxPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBaseCheckBoxPreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.util.AttributeSet;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBasePreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java
similarity index 97%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBasePreference.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java
index 0d3dbf5..04837e7 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeBasePreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeBasePreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.content.res.ColorStateList;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreference.java
similarity index 98%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreference.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreference.java
index fef5c9b..52e32b9 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeImageViewPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeImageViewPreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.graphics.Color;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeSwitchPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
similarity index 96%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeSwitchPreference.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
index 4d48665..0038b0b 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ChromeSwitchPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ChromeSwitchPreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.text.TextUtils;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ExpandablePreferenceGroup.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java
similarity index 98%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ExpandablePreferenceGroup.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java
index 061f36e5..bde1bfaf 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/ExpandablePreferenceGroup.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/ExpandablePreferenceGroup.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java
similarity index 98%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java
index a5b720e0..557c58d 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/SpinnerPreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/SpinnerPreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.content.res.TypedArray;
diff --git a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/TextMessagePreference.java b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/TextMessagePreference.java
similarity index 94%
rename from chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/TextMessagePreference.java
rename to components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/TextMessagePreference.java
index 8b97910..2fedebd 100644
--- a/chrome/browser/settings/android/widget/java/src/org/chromium/chrome/browser/settings/TextMessagePreference.java
+++ b/components/browser_ui/settings/android/widget/java/src/org/chromium/components/browser_ui/settings/TextMessagePreference.java
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-package org.chromium.chrome.browser.settings;
+package org.chromium.components.browser_ui.settings;
 
 import android.content.Context;
 import android.text.method.LinkMovementMethod;
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd
index debe6a1..914afe255 100644
--- a/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -349,6 +349,25 @@
         Certificate viewer
       </message>
 
+      <!-- Settings UI -->
+      <message name="IDS_MANAGED_BY_YOUR_ORGANIZATION" desc="Popup message when the user clicks a UI element that has been disabled by enterprise policy.">
+        This setting is enforced by your administrator.
+      </message>
+      <message name="IDS_MANAGED_BY_YOUR_PARENTS" desc="Popup message when the user clicks a UI element that has been disabled by their parents.">
+        Managed by your parents
+      </message>
+      <message name="IDS_MANAGED_BY_YOUR_PARENT" desc="Popup message when the user clicks a UI element that has been disabled by their parent.">
+        Managed by your parent
+      </message>
+      <message name="IDS_MANAGED_SETTINGS_CANNOT_BE_RESET" desc="Popup message when the user clicks a UI element that resets a list of settings that include managed settings.">
+        Managed settings cannot be reset
+      </message>
+      <message name="IDS_ACCESSIBILITY_EXPANDED_GROUP" desc="The accessibility text to read when the selected widget is expanded.">
+        Expanded - click to collapse.
+      </message>
+      <message name="IDS_ACCESSIBILITY_COLLAPSED_GROUP" desc="The accessibility text to read when the selected widget is collapsed.">
+        Collapsed - click to expand.
+      </message>
     </messages>
   </release>
 </grit>
diff --git a/chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGED_BY_YOUR_ORGANIZATION.png.sha1 b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_MANAGED_BY_YOUR_ORGANIZATION.png.sha1
similarity index 100%
rename from chrome/browser/ui/android/strings/android_chrome_strings_grd/IDS_MANAGED_BY_YOUR_ORGANIZATION.png.sha1
rename to components/browser_ui/strings/android/browser_ui_strings_grd/IDS_MANAGED_BY_YOUR_ORGANIZATION.png.sha1
diff --git a/components/dom_distiller/core/url_utils.h b/components/dom_distiller/core/url_utils.h
index 8f94a56..a17b930 100644
--- a/components/dom_distiller/core/url_utils.h
+++ b/components/dom_distiller/core/url_utils.h
@@ -19,12 +19,20 @@
 const GURL GetDistillerViewUrlFromEntryId(const std::string& scheme,
                                           const std::string& entry_id);
 
-// Returns the URL for viewing distilled content for a URL.
+// Returns the URL for viewing distilled content for |view_url|. This URL should
+// not be displayed to end users (except in DevTools and view-source). Instead,
+// users should always be shown the original page URL minus the http or https
+// scheme in the omnibox (i.e. in LocationBarModel::GetFormattedURL()).
+// A distilled page's true URL, the distiller view URL, should be returned
+// from WebContents::GetLastCommittedURL() and WebContents::GetVisibleURL().
+// This has the chrome-distiller scheme and the form
+// chrome-distiller://<hash>?<params>, where <params> are generated from
+// |view_url| and |start_time_ms|.
 const GURL GetDistillerViewUrlFromUrl(const std::string& scheme,
                                       const GURL& view_url,
                                       int64_t start_time_ms = 0);
 
-// Returns the original URL from the distilled URL.
+// Returns the original article's URL from the distilled URL.
 // If |distilled_url| is not distilled, it is returned as is.
 // If |distilled_url| looks like distilled, but no original URL can be found,
 // an empty, invalid URL is returned.
diff --git a/components/management_strings.grdp b/components/management_strings.grdp
index 3665e459..973f3b59 100644
--- a/components/management_strings.grdp
+++ b/components/management_strings.grdp
@@ -170,15 +170,15 @@
     Performance data and crash reports
   </message>
 
-  <!-- Strings related to Enterprise Service Hooks -->
-  <message name="IDS_MANAGEMENT_THREAT_PROTECTION" desc="Title of the Enterprise Service Hooks section of the page">
-    Enterprise Service Hooks
+  <!-- Strings related to Chrome Enterprise Connectors -->
+  <message name="IDS_MANAGEMENT_THREAT_PROTECTION" desc="Title of the Chrome Enterprise Connectors section of the page">
+    Chrome Enterprise Connectors
   </message>
-  <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION" desc="Description of the Enterprise Service Hooks section of the page">
-    Your administrator has enabled Enterprise Service Hooks on your browser. These hooks have access to some of your data.
+  <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION" desc="Description of the Chrome Enterprise Connectors section of the page">
+    Your administrator has enabled Chrome Enterprise Connectors on your browser. These connectors have access to some of your data.
   </message>
-  <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY" desc="Description of the Enterprise Service Hooks section of the page">
-    <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> has enabled Enterprise Service Hooks on your browser. These hooks have access to some of your data.
+  <message name="IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY" desc="Description of the Chrome Enterprise Connectors section of the page">
+    <ph name="ENROLLMENT_DOMAIN">$1<ex>example.com</ex></ph> has enabled Chrome Enterprise Connectors on your browser. These connectors have access to some of your data.
   </message>
   <message name="IDS_MANAGEMENT_DATA_LOSS_PREVENTION_NAME" desc="Name for the data loss prevention feature">
     Data Loss Prevention
@@ -196,6 +196,6 @@
     Enterprise Reporting
   </message>
   <message name="IDS_MANAGEMENT_ENTERPRISE_REPORTING_PERMISSIONS" desc="Permissions description for the enterprise reporting feature">
-    Share data about security events that have been flagged by Enterprise Service Hooks with your administrator. This may include URLs of pages you visit, file names or metadata, and the username you use to sign in to your device and Chrome.
+    Share data about security events that have been flagged by Chrome Enterprise Connectors with your administrator. This may include URLs of pages you visit, file names or metadata, and the username you use to sign in to your device and Chrome.
   </message>
 </grit-part>
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_ENTERPRISE_REPORTING_PERMISSIONS.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_ENTERPRISE_REPORTING_PERMISSIONS.png.sha1
index 4356e4da..7681b5a 100644
--- a/components/management_strings_grdp/IDS_MANAGEMENT_ENTERPRISE_REPORTING_PERMISSIONS.png.sha1
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_ENTERPRISE_REPORTING_PERMISSIONS.png.sha1
@@ -1 +1 @@
-cd2f24278f9d1d537da4e38ecee1d42f7f57c352
\ No newline at end of file
+5505d954ef9c93b2b717d4d3bb7e3de3a55fb778
\ No newline at end of file
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION.png.sha1
index 4356e4da..7681b5a 100644
--- a/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION.png.sha1
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION.png.sha1
@@ -1 +1 @@
-cd2f24278f9d1d537da4e38ecee1d42f7f57c352
\ No newline at end of file
+5505d954ef9c93b2b717d4d3bb7e3de3a55fb778
\ No newline at end of file
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION.png.sha1
index 4356e4da..7681b5a 100644
--- a/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION.png.sha1
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION.png.sha1
@@ -1 +1 @@
-cd2f24278f9d1d537da4e38ecee1d42f7f57c352
\ No newline at end of file
+5505d954ef9c93b2b717d4d3bb7e3de3a55fb778
\ No newline at end of file
diff --git a/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY.png.sha1 b/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY.png.sha1
index 4356e4da..7681b5a 100644
--- a/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY.png.sha1
+++ b/components/management_strings_grdp/IDS_MANAGEMENT_THREAT_PROTECTION_DESCRIPTION_BY.png.sha1
@@ -1 +1 @@
-cd2f24278f9d1d537da4e38ecee1d42f7f57c352
\ No newline at end of file
+5505d954ef9c93b2b717d4d3bb7e3de3a55fb778
\ No newline at end of file
diff --git a/components/media_message_center/media_controls_progress_view_unittest.cc b/components/media_message_center/media_controls_progress_view_unittest.cc
index 9f791ca..82e76c8c 100644
--- a/components/media_message_center/media_controls_progress_view_unittest.cc
+++ b/components/media_message_center/media_controls_progress_view_unittest.cc
@@ -120,8 +120,8 @@
             base::ASCIIToUTF16("05:00"));
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(30));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(30));
+  task_environment()->RunUntilIdle();
 
   EXPECT_EQ(progress_view_->duration_for_testing(),
             base::ASCIIToUTF16("10:00"));
@@ -143,8 +143,8 @@
             base::ASCIIToUTF16("05:00"));
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(15));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(15));
+  task_environment()->RunUntilIdle();
 
   EXPECT_EQ(progress_view_->duration_for_testing(),
             base::ASCIIToUTF16("10:00"));
@@ -166,8 +166,8 @@
             base::ASCIIToUTF16("05:00"));
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(60));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(60));
+  task_environment()->RunUntilIdle();
 
   EXPECT_EQ(progress_view_->duration_for_testing(),
             base::ASCIIToUTF16("10:00"));
@@ -189,8 +189,8 @@
             base::ASCIIToUTF16("05:00"));
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(30));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(30));
+  task_environment()->RunUntilIdle();
 
   EXPECT_EQ(progress_view_->duration_for_testing(),
             base::ASCIIToUTF16("10:00"));
@@ -213,8 +213,8 @@
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
 
   // Move forward in time past the duration.
-  task_environment_->FastForwardBy(base::TimeDelta::FromMinutes(6));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(6));
+  task_environment()->RunUntilIdle();
 
   // Verify the progress does not go past the duration.
   EXPECT_EQ(progress_view_->duration_for_testing(),
@@ -238,8 +238,8 @@
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
 
   // Move forward in time before the start using negative playback rate.
-  task_environment_->FastForwardBy(base::TimeDelta::FromMinutes(6));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(6));
+  task_environment()->RunUntilIdle();
 
   // Verify the progress does not go below 0.
   EXPECT_EQ(progress_view_->duration_for_testing(),
@@ -262,8 +262,8 @@
             base::ASCIIToUTF16("05:00"));
   EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromMinutes(6));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(6));
+  task_environment()->RunUntilIdle();
 
   // Verify the progress does not change while media is paused.
   EXPECT_EQ(progress_view_->duration_for_testing(),
diff --git a/components/media_message_center/media_notification_view_impl_unittest.cc b/components/media_message_center/media_notification_view_impl_unittest.cc
index 4815360f..44b9b4e 100644
--- a/components/media_message_center/media_notification_view_impl_unittest.cc
+++ b/components/media_message_center/media_notification_view_impl_unittest.cc
@@ -306,8 +306,7 @@
   }
 
   void AdvanceClockMilliseconds(int milliseconds) {
-    ASSERT_TRUE(task_environment_.has_value());
-    task_environment_->FastForwardBy(
+    task_environment()->FastForwardBy(
         base::TimeDelta::FromMilliseconds(milliseconds));
   }
 
diff --git a/components/omnibox/browser/BUILD.gn b/components/omnibox/browser/BUILD.gn
index f802881..4757f16e 100644
--- a/components/omnibox/browser/BUILD.gn
+++ b/components/omnibox/browser/BUILD.gn
@@ -225,6 +225,7 @@
     ":in_memory_url_index_cache_proto",
     "//components/bookmarks/browser",
     "//components/component_updater",
+    "//components/dom_distiller/core:core",
     "//components/favicon/core",
     "//components/favicon_base",
     "//components/keyed_service/core",
@@ -470,6 +471,7 @@
     "//base/test:test_support",
     "//components/bookmarks/browser",
     "//components/bookmarks/test",
+    "//components/dom_distiller/core:core",
     "//components/favicon/core/test:test_support",
     "//components/history/core/test",
     "//components/open_from_clipboard:test_support",
diff --git a/components/omnibox/browser/DEPS b/components/omnibox/browser/DEPS
index c368276..5a88c3cf 100644
--- a/components/omnibox/browser/DEPS
+++ b/components/omnibox/browser/DEPS
@@ -2,6 +2,7 @@
   "+components/bookmarks/browser",
   "+components/bookmarks/test",
   "+components/component_updater",
+  "+components/dom_distiller/core",
   "+components/embedder_support/android/java/src/org/chromium/components/embedder_support/util",
   "+components/favicon/core",
   "+components/favicon_base",
diff --git a/components/omnibox/browser/location_bar_model_impl.cc b/components/omnibox/browser/location_bar_model_impl.cc
index 102451a9..2e05546 100644
--- a/components/omnibox/browser/location_bar_model_impl.cc
+++ b/components/omnibox/browser/location_bar_model_impl.cc
@@ -10,6 +10,8 @@
 #include "base/strings/utf_string_conversions.h"
 #include "base/time/time.h"
 #include "build/build_config.h"
+#include "components/dom_distiller/core/url_constants.h"
+#include "components/dom_distiller/core/url_utils.h"
 #include "components/omnibox/browser/autocomplete_classifier.h"
 #include "components/omnibox/browser/autocomplete_match.h"
 #include "components/omnibox/browser/buildflags.h"
@@ -55,6 +57,11 @@
     format_types |= url_formatter::kFormatUrlTrimAfterHost;
   }
 
+  // Early exit to prevent elision of URLs when relevant extension is enabled.
+  if (delegate_->ShouldPreventElision()) {
+    return GetFormattedURL(format_types);
+  }
+
 #if defined(OS_IOS)
   format_types |= url_formatter::kFormatUrlTrimAfterHost;
 #endif
@@ -76,14 +83,25 @@
   if (!ShouldDisplayURL())
     return base::string16{};
 
-  // Reset |format_types| to prevent elision of URLs when relevant extension or
-  // pref is enabled.
-  if (delegate_->ShouldPreventElision()) {
-    format_types = url_formatter::kFormatUrlOmitDefaults &
-                   ~url_formatter::kFormatUrlOmitHTTP;
+  GURL url(GetURL());
+  // Special handling for dom-distiller:. Instead of showing internal reader
+  // mode URLs, show the original article URL without a http or https scheme.
+  // Note that this does not disallow the user from copy-pasting the reader
+  // mode URL, or from seeing it in the view-source url. Note that this also
+  // impacts GetFormattedFullURL which uses GetFormattedURL as a helper.
+  // Virtual URLs were not a good solution for Reader Mode URLs because some
+  // security UI is based off of the virtual URL rather than the original URL,
+  // and Reader Mode has its own security chip. In addition virtual URLs would
+  // add a lot of complexity around passing necessary URL parameters to the
+  // Reader Mode pages.
+  if (url.SchemeIs(dom_distiller::kDomDistillerScheme)) {
+    // Ensure that HTTPS and HTTP will be removed. Reader mode should not
+    // display a scheme, and should only run on HTTP/HTTPS pages.
+    format_types |= url_formatter::kFormatUrlOmitHTTP;
+    format_types |= url_formatter::kFormatUrlOmitHTTPS;
+    url = dom_distiller::url_utils::GetOriginalUrlFromDistillerUrl(url);
   }
 
-  GURL url(GetURL());
   // Note that we can't unescape spaces here, because if the user copies this
   // and pastes it into another program, that program may think the URL ends at
   // the space.
diff --git a/components/omnibox/browser/location_bar_model_impl_unittest.cc b/components/omnibox/browser/location_bar_model_impl_unittest.cc
index 8a7b20e5..9d9dcbe 100644
--- a/components/omnibox/browser/location_bar_model_impl_unittest.cc
+++ b/components/omnibox/browser/location_bar_model_impl_unittest.cc
@@ -8,6 +8,8 @@
 #include "base/test/scoped_feature_list.h"
 #include "base/test/task_environment.h"
 #include "build/build_config.h"
+#include "components/dom_distiller/core/url_constants.h"
+#include "components/dom_distiller/core/url_utils.h"
 #include "components/omnibox/browser/location_bar_model_delegate.h"
 #include "components/omnibox/browser/test_omnibox_client.h"
 #include "components/omnibox/common/omnibox_features.h"
@@ -119,6 +121,43 @@
             model()->GetURLForDisplay());
 }
 
+TEST_F(LocationBarModelImplTest, FormatsReaderModeUrls) {
+  const GURL http_url("http://www.example.com/article.html");
+  // Get the real article's URL shown to the user.
+  delegate()->SetURL(http_url);
+  base::string16 originalDisplayUrl = model()->GetURLForDisplay();
+  base::string16 originalFormattedFullUrl = model()->GetFormattedFullURL();
+  // We expect that they don't start with "http://." We want the reader mode
+  // URL shown to the user to be the same as this original URL.
+#ifdef OS_IOS
+  EXPECT_EQ(base::ASCIIToUTF16("example.com/TestSuffix"), originalDisplayUrl);
+#else
+  EXPECT_EQ(base::ASCIIToUTF16("example.com/article.html/TestSuffix"),
+            originalDisplayUrl);
+#endif
+  EXPECT_EQ(base::ASCIIToUTF16("www.example.com/article.html/TestSuffix"),
+            originalFormattedFullUrl);
+
+  GURL distilled = dom_distiller::url_utils::GetDistillerViewUrlFromUrl(
+      dom_distiller::kDomDistillerScheme, http_url);
+  // Ensure the test is set up properly by checking the reader mode URL has
+  // the reader mode scheme.
+  EXPECT_EQ(dom_distiller::kDomDistillerScheme, distilled.scheme());
+  delegate()->SetURL(distilled);
+
+  // The user should see the same URL seen for the original article.
+  EXPECT_EQ(originalDisplayUrl, model()->GetURLForDisplay());
+  EXPECT_EQ(originalFormattedFullUrl, model()->GetFormattedFullURL());
+
+  // Similarly, https scheme should also be hidden.
+  const GURL https_url("https://www.example.com/article.html");
+  distilled = dom_distiller::url_utils::GetDistillerViewUrlFromUrl(
+      dom_distiller::kDomDistillerScheme, https_url);
+  delegate()->SetURL(distilled);
+  EXPECT_EQ(originalDisplayUrl, model()->GetURLForDisplay());
+  EXPECT_EQ(originalFormattedFullUrl, model()->GetFormattedFullURL());
+}
+
 // TODO(https://crbug.com/1010418): Fix flakes on linux_chromium_asan_rel_ng and
 // re-enable this test.
 #if defined(OS_LINUX)
diff --git a/components/password_manager/core/browser/password_autofill_manager.cc b/components/password_manager/core/browser/password_autofill_manager.cc
index e8d9c170..e6b3d50 100644
--- a/components/password_manager/core/browser/password_autofill_manager.cc
+++ b/components/password_manager/core/browser/password_autofill_manager.cc
@@ -201,9 +201,8 @@
 
 // Entry for opting in to password account storage and then filling.
 autofill::Suggestion CreateEntryToOptInToAccountStorageThenFill() {
-  // TODO(crbug.com/1062344): Add proper (translated) string.
   autofill::Suggestion suggestion(
-      base::ASCIIToUTF16("Use passwords stored in your Google account"));
+      l10n_util::GetStringUTF16(IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORE));
   suggestion.frontend_id =
       autofill::POPUP_ITEM_ID_PASSWORD_ACCOUNT_STORAGE_OPT_IN;
   return suggestion;
@@ -211,9 +210,8 @@
 
 // Entry for opting in to password account storage and then generating password.
 autofill::Suggestion CreateEntryToOptInToAccountStorageThenGenerate() {
-  // TODO(crbug.com/1062344): Add proper (translated) string.
-  autofill::Suggestion suggestion(base::ASCIIToUTF16(
-      "Use your Google account to generate a strong password"));
+  autofill::Suggestion suggestion(l10n_util::GetStringUTF16(
+      IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORED_GENERATION));
   suggestion.frontend_id =
       autofill::POPUP_ITEM_ID_PASSWORD_ACCOUNT_STORAGE_OPT_IN_AND_GENERATE;
   return suggestion;
diff --git a/components/password_manager/core/browser/password_generation_manager.cc b/components/password_manager/core/browser/password_generation_manager.cc
index 2dff14c..93622ac8 100644
--- a/components/password_manager/core/browser/password_generation_manager.cc
+++ b/components/password_manager/core/browser/password_generation_manager.cc
@@ -9,7 +9,6 @@
 
 #include "base/callback.h"
 #include "base/time/default_clock.h"
-#include "components/password_manager/core/browser/form_fetcher.h"
 #include "components/password_manager/core/browser/form_saver.h"
 #include "components/password_manager/core/browser/password_form_manager_for_ui.h"
 #include "components/password_manager/core/browser/password_manager_client.h"
@@ -200,19 +199,20 @@
 
 void PasswordGenerationManager::GeneratedPasswordAccepted(
     PasswordForm generated,
-    const FormFetcher& fetcher,
+    const std::vector<const PasswordForm*>& non_federated_matches,
+    const std::vector<const PasswordForm*>& federated_matches,
     base::WeakPtr<PasswordManagerDriver> driver) {
   // Clear the username value if there are already saved credentials with
   // the same username in order to prevent overwriting.
-  std::vector<const PasswordForm*> matches = fetcher.GetNonFederatedMatches();
-  if (FindUsernameConflict(generated, matches)) {
+  if (FindUsernameConflict(generated, non_federated_matches)) {
     generated.username_value.clear();
-    const PasswordForm* conflict = FindUsernameConflict(generated, matches);
+    const PasswordForm* conflict =
+        FindUsernameConflict(generated, non_federated_matches);
     if (conflict) {
       LogGenerationPresaveConflict(
           GenerationPresaveConflict::kConflictWithEmptyUsername);
       auto bubble_launcher = std::make_unique<PasswordDataForUI>(
-          std::move(generated), matches, fetcher.GetFederatedMatches(),
+          std::move(generated), non_federated_matches, federated_matches,
           base::BindRepeating(&PasswordGenerationManager::OnPresaveBubbleResult,
                               weak_factory_.GetWeakPtr(), std::move(driver)));
       client_->PromptUserToSaveOrUpdatePassword(std::move(bubble_launcher),
diff --git a/components/password_manager/core/browser/password_generation_manager.h b/components/password_manager/core/browser/password_generation_manager.h
index 8b2a7ba..0d7b6a0d 100644
--- a/components/password_manager/core/browser/password_generation_manager.h
+++ b/components/password_manager/core/browser/password_generation_manager.h
@@ -15,7 +15,6 @@
 
 namespace password_manager {
 
-class FormFetcher;
 class FormSaver;
 class PasswordManagerClient;
 class PasswordManagerDriver;
@@ -37,19 +36,22 @@
     return presaved_->password_value;
   }
 
-  // Called when user wants to start generation flow for |generated|. If there
-  // is no username conflict, the message is synchronously passed to |driver|.
-  // |fetcher| to fill that UI with correct data.
-  // Otherwise, the UI on the client is invoked to ask for overwrite permission.
-  // There is one corner case that is still not covered.
-  // The user had the current password saved with empty username.
+  // Called when user wants to start generation flow for |generated|.
+  // |non_federated_matches| and |federated_matches| are used to determine
+  // whether there is a username conflict. If there is none, the message is
+  // synchronously passed to |driver|. Otherwise, the UI on the client is
+  // invoked to ask for overwrite permission. There is one corner case that is
+  // still not covered. The user had the current password saved with empty
+  // username.
   // - The change password form has no username.
   // - The user generates a password and sees the bubble with an empty username.
   // - The user clicks 'Update'.
   // - The actual form submission doesn't succeed for some reason.
-  void GeneratedPasswordAccepted(autofill::PasswordForm generated,
-                                 const FormFetcher& fetcher,
-                                 base::WeakPtr<PasswordManagerDriver> driver);
+  void GeneratedPasswordAccepted(
+      autofill::PasswordForm generated,
+      const std::vector<const autofill::PasswordForm*>& non_federated_matches,
+      const std::vector<const autofill::PasswordForm*>& federated_matches,
+      base::WeakPtr<PasswordManagerDriver> driver);
 
   // Called when generated password is accepted or changed by user.
   void PresaveGeneratedPassword(
diff --git a/components/password_manager/core/browser/password_generation_manager_unittest.cc b/components/password_manager/core/browser/password_generation_manager_unittest.cc
index 0174c386..0283127a 100644
--- a/components/password_manager/core/browser/password_generation_manager_unittest.cc
+++ b/components/password_manager/core/browser/password_generation_manager_unittest.cc
@@ -167,8 +167,9 @@
 
   EXPECT_CALL(client_, PromptUserToSaveOrUpdatePasswordMock(true))
       .WillOnce(testing::Return(true));
-  manager().GeneratedPasswordAccepted(std::move(generated), fetcher,
-                                      std::move(driver));
+  manager().GeneratedPasswordAccepted(
+      std::move(generated), fetcher.GetNonFederatedMatches(),
+      fetcher.GetFederatedMatches(), std::move(driver));
   return client_.MoveForm();
 }
 
@@ -181,8 +182,9 @@
   FakeFormFetcher fetcher;
 
   EXPECT_CALL(driver, GeneratedPasswordAccepted(generated.password_value));
-  manager().GeneratedPasswordAccepted(std::move(generated), fetcher,
-                                      driver.AsWeakPtr());
+  manager().GeneratedPasswordAccepted(
+      std::move(generated), fetcher.GetNonFederatedMatches(),
+      fetcher.GetFederatedMatches(), driver.AsWeakPtr());
   EXPECT_FALSE(manager().HasGeneratedPassword());
   histogram_tester.ExpectUniqueSample(
       "PasswordGeneration.PresaveConflict",
@@ -202,8 +204,9 @@
   fetcher.SetNonFederated({&saved});
 
   EXPECT_CALL(driver, GeneratedPasswordAccepted(generated.password_value));
-  manager().GeneratedPasswordAccepted(std::move(generated), fetcher,
-                                      driver.AsWeakPtr());
+  manager().GeneratedPasswordAccepted(
+      std::move(generated), fetcher.GetNonFederatedMatches(),
+      fetcher.GetFederatedMatches(), driver.AsWeakPtr());
   EXPECT_FALSE(manager().HasGeneratedPassword());
   histogram_tester.ExpectUniqueSample(
       "PasswordGeneration.PresaveConflict",
diff --git a/components/password_manager/core/browser/password_save_manager_impl.cc b/components/password_manager/core/browser/password_save_manager_impl.cc
index 7f1dc904..4417c7e3 100644
--- a/components/password_manager/core/browser/password_save_manager_impl.cc
+++ b/components/password_manager/core/browser/password_save_manager_impl.cc
@@ -301,8 +301,9 @@
     PasswordForm parsed_form,
     base::WeakPtr<PasswordManagerDriver> driver) {
   generation_manager_ = std::make_unique<PasswordGenerationManager>(client_);
-  generation_manager_->GeneratedPasswordAccepted(std::move(parsed_form),
-                                                 *form_fetcher_, driver);
+  generation_manager_->GeneratedPasswordAccepted(
+      std::move(parsed_form), form_fetcher_->GetNonFederatedMatches(),
+      form_fetcher_->GetFederatedMatches(), driver);
 }
 
 void PasswordSaveManagerImpl::PasswordNoLongerGenerated() {
diff --git a/components/password_manager_strings.grdp b/components/password_manager_strings.grdp
index 5846134..f89fcbc 100644
--- a/components/password_manager_strings.grdp
+++ b/components/password_manager_strings.grdp
@@ -34,6 +34,12 @@
   <message name="IDS_PASSWORD_MANAGER_EMPTY_LOGIN" desc="A placeholder for the 'Manage Passwords' bubble's empty username label">
     No username
   </message>
+  <message name="IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORE" desc="A menu item allowing signed-in users to opt into using account-stored passwords.">
+    Use passwords stored in your Google account
+  </message>
+  <message name="IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORED_GENERATION" desc="A menu item allowing signed-in users to opt into the account store and generate a password.">
+    Use your Google account to generate a strong password
+  </message>
   <if expr="not use_titlecase">
     <message name="IDS_PASSWORD_MANAGER_MANAGE_PASSWORDS" desc="The menu item in the password field drop down that opens the list of saved passwords.">
       Manage passwords…
diff --git a/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORE.png.sha1 b/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORE.png.sha1
new file mode 100644
index 0000000..d826102
--- /dev/null
+++ b/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORE.png.sha1
@@ -0,0 +1 @@
+8f84fa679e3583eb4bc1cadd68e4fd91b4952016
\ No newline at end of file
diff --git a/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORED_GENERATION.png.sha1 b/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORED_GENERATION.png.sha1
new file mode 100644
index 0000000..7812372
--- /dev/null
+++ b/components/password_manager_strings_grdp/IDS_PASSWORD_MANAGER_OPT_INTO_ACCOUNT_STORED_GENERATION.png.sha1
@@ -0,0 +1 @@
+bc24abb9b5fceb28199f375ee3643b1b8d02f6e9
\ No newline at end of file
diff --git a/components/policy/BUILD.gn b/components/policy/BUILD.gn
index d6eb7b0..61f560f 100644
--- a/components/policy/BUILD.gn
+++ b/components/policy/BUILD.gn
@@ -236,6 +236,8 @@
       rebase_path(policy_templates_plist_path, root_build_dir),
       "--plist_strings",
       rebase_path(policy_templates_plist_strings_path, root_build_dir),
+      "--jamf",
+      rebase_path(policy_templates_jamf_path, root_build_dir),
       "-D",
       "mac_bundle_id=$chrome_mac_bundle_id",
     ]
diff --git a/components/policy/core/common/cloud/cloud_policy_core.cc b/components/policy/core/common/cloud/cloud_policy_core.cc
index 82c79e0..16fb4695b 100644
--- a/components/policy/core/common/cloud/cloud_policy_core.cc
+++ b/components/policy/core/common/cloud/cloud_policy_core.cc
@@ -13,6 +13,7 @@
 #include "components/policy/core/common/cloud/cloud_policy_refresh_scheduler.h"
 #include "components/policy/core/common/cloud/cloud_policy_service.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
 #include "components/policy/core/common/remote_commands/remote_commands_factory.h"
 #include "components/policy/core/common/remote_commands/remote_commands_service.h"
 #include "components/prefs/pref_service.h"
@@ -62,12 +63,13 @@
 }
 
 void CloudPolicyCore::StartRemoteCommandsService(
-    std::unique_ptr<RemoteCommandsFactory> factory) {
+    std::unique_ptr<RemoteCommandsFactory> factory,
+    PolicyInvalidationScope scope) {
   DCHECK(client_);
   DCHECK(factory);
 
   remote_commands_service_ = std::make_unique<RemoteCommandsService>(
-      std::move(factory), client_.get(), store_);
+      std::move(factory), client_.get(), store_, scope);
 
   // Do an initial remote commands fetch immediately.
   remote_commands_service_->FetchRemoteCommands();
diff --git a/components/policy/core/common/cloud/cloud_policy_core.h b/components/policy/core/common/cloud/cloud_policy_core.h
index 470abfb..afb4d04 100644
--- a/components/policy/core/common/cloud/cloud_policy_core.h
+++ b/components/policy/core/common/cloud/cloud_policy_core.h
@@ -11,6 +11,7 @@
 #include "base/macros.h"
 #include "base/memory/ref_counted.h"
 #include "base/observer_list.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
 #include "components/policy/policy_export.h"
 #include "components/prefs/pref_member.h"
 #include "services/network/public/cpp/network_connection_tracker.h"
@@ -99,7 +100,8 @@
   // to fetch commands immediately, thus requiring the cloud policy client to
   // be registered.
   void StartRemoteCommandsService(
-      std::unique_ptr<RemoteCommandsFactory> factory);
+      std::unique_ptr<RemoteCommandsFactory> factory,
+      PolicyInvalidationScope scope);
 
   // Requests a policy refresh to be performed soon. This may apply throttling,
   // and the request may not be immediately sent.
diff --git a/components/policy/core/common/cloud/enterprise_metrics.cc b/components/policy/core/common/cloud/enterprise_metrics.cc
index 835bae3be..8438de4 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.cc
+++ b/components/policy/core/common/cloud/enterprise_metrics.cc
@@ -49,4 +49,84 @@
 const char kMetricRemoteCommandInvalidationsRegistrationResult[] =
     "Enterprise.RemoteCommandInvalidationsRegistrationResult";
 
+const char kMetricUserRemoteCommandReceived[] =
+    "Enterprise.UserRemoteCommand.Received";
+
+const char kMetricUserUnsignedRemoteCommandReceived[] =
+    "Enterprise.UserRemoteCommand.Received.Unsigned";
+
+// Expands to:
+// Enterprise.UserRemoteCommand.Executed.CommandEchoTest
+// Enterprise.UserRemoteCommand.Executed.DeviceReboot
+// Enterprise.UserRemoteCommand.Executed.DeviceScreenshot
+// Enterprise.UserRemoteCommand.Executed.DeviceSetVolume
+// Enterprise.UserRemoteCommand.Executed.DeviceStartCrdSession
+// Enterprise.UserRemoteCommand.Executed.DeviceFetchStatus
+// Enterprise.UserRemoteCommand.Executed.UserArcCommand
+// Enterprise.UserRemoteCommand.Executed.DeviceWipeUsers
+// Enterprise.UserRemoteCommand.Executed.DeviceRefreshEnterpriseMachineCertificate
+// Enterprise.UserRemoteCommand.Executed.DeviceRemotePowerwash
+// Enterprise.UserRemoteCommand.Executed.DeviceGetAvailableDiagnosticRoutines
+// Enterprise.UserRemoteCommand.Executed.DeviceRunDiagnosticRoutine
+// Enterprise.UserRemoteCommand.Executed.DeviceGetDiagnosticRoutineUpdate
+const char kMetricUserRemoteCommandExecutedTemplate[] =
+    "Enterprise.UserRemoteCommand.Executed.%s";
+
+// Expands to:
+// Enterprise.UserRemoteCommand.Executed.Unsigned.CommandEchoTest
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceReboot
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceScreenshot
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceSetVolume
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceStartCrdSession
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceFetchStatus
+// Enterprise.UserRemoteCommand.Executed.Unsigned.UserArcCommand
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceWipeUsers
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceRefreshEnterpriseMachineCertificate
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceRemotePowerwash
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceGetAvailableDiagnosticRoutines
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceRunDiagnosticRoutine
+// Enterprise.UserRemoteCommand.Executed.Unsigned.DeviceGetDiagnosticRoutineUpdate
+const char kMetricUserUnsignedRemoteCommandExecutedTemplate[] =
+    "Enterprise.UserRemoteCommand.Executed.Unsigned.%s";
+
+const char kMetricDeviceRemoteCommandReceived[] =
+    "Enterprise.DeviceRemoteCommand.Received";
+
+const char kMetricDeviceUnsignedRemoteCommandReceived[] =
+    "Enterprise.DeviceRemoteCommand.Received.Unsigned";
+
+// Expands to:
+// Enterprise.DeviceRemoteCommand.Executed.CommandEchoTest
+// Enterprise.DeviceRemoteCommand.Executed.DeviceReboot
+// Enterprise.DeviceRemoteCommand.Executed.DeviceScreenshot
+// Enterprise.DeviceRemoteCommand.Executed.DeviceSetVolume
+// Enterprise.DeviceRemoteCommand.Executed.DeviceStartCrdSession
+// Enterprise.DeviceRemoteCommand.Executed.DeviceFetchStatus
+// Enterprise.DeviceRemoteCommand.Executed.UserArcCommand
+// Enterprise.DeviceRemoteCommand.Executed.DeviceWipeUsers
+// Enterprise.DeviceRemoteCommand.Executed.DeviceRefreshEnterpriseMachineCertificate
+// Enterprise.DeviceRemoteCommand.Executed.DeviceRemotePowerwash
+// Enterprise.DeviceRemoteCommand.Executed.DeviceGetAvailableDiagnosticRoutines
+// Enterprise.DeviceRemoteCommand.Executed.DeviceRunDiagnosticRoutine
+// Enterprise.DeviceRemoteCommand.Executed.DeviceGetDiagnosticRoutineUpdate
+const char kMetricDeviceRemoteCommandExecutedTemplate[] =
+    "Enterprise.DeviceRemoteCommand.Executed.%s";
+
+// Expands to:
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.CommandEchoTest
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceReboot
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceScreenshot
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceSetVolume
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceStartCrdSession
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceFetchStatus
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.UserArcCommand
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceWipeUsers
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceRefreshEnterpriseMachineCertificate
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceRemotePowerwash
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceGetAvailableDiagnosticRoutines
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceRunDiagnosticRoutine
+// Enterprise.DeviceRemoteCommand.Executed.Unsigned.DeviceGetDiagnosticRoutineUpdate
+const char kMetricDeviceUnsignedRemoteCommandExecutedTemplate[] =
+    "Enterprise.DeviceRemoteCommand.Executed.Unsigned.%s";
+
 }  // namespace policy
diff --git a/components/policy/core/common/cloud/enterprise_metrics.h b/components/policy/core/common/cloud/enterprise_metrics.h
index 90b55b7..d260dfb 100644
--- a/components/policy/core/common/cloud/enterprise_metrics.h
+++ b/components/policy/core/common/cloud/enterprise_metrics.h
@@ -223,7 +223,8 @@
 // from the same enum above can be triggered in different files, and must use
 // the same UMA histogram name.
 // Metrics name from UMA dashboard cloud be used in codesearch as is, so please
-// keep the names without format specifiers (e.g. %s).
+// keep the names without format specifiers (e.g. %s) or add a comment how the
+// name could be expanded.
 POLICY_EXPORT extern const char kMetricUserPolicyRefresh[];
 POLICY_EXPORT extern const char kMetricUserPolicyRefreshFcm[];
 POLICY_EXPORT extern const char kMetricUserPolicyInvalidations[];
@@ -250,6 +251,18 @@
 POLICY_EXPORT extern const char
     kMetricRemoteCommandInvalidationsRegistrationResult[];
 
+POLICY_EXPORT extern const char kMetricUserRemoteCommandReceived[];
+POLICY_EXPORT extern const char kMetricUserUnsignedRemoteCommandReceived[];
+POLICY_EXPORT extern const char kMetricUserRemoteCommandExecutedTemplate[];
+POLICY_EXPORT extern const char
+    kMetricUserUnsignedRemoteCommandExecutedTemplate[];
+
+POLICY_EXPORT extern const char kMetricDeviceRemoteCommandReceived[];
+POLICY_EXPORT extern const char kMetricDeviceUnsignedRemoteCommandReceived[];
+POLICY_EXPORT extern const char kMetricDeviceRemoteCommandExecutedTemplate[];
+POLICY_EXPORT extern const char
+    kMetricDeviceUnsignedRemoteCommandExecutedTemplate[];
+
 }  // namespace policy
 
 #endif  // COMPONENTS_POLICY_CORE_COMMON_CLOUD_ENTERPRISE_METRICS_H_
diff --git a/components/policy/core/common/remote_commands/remote_command_job.h b/components/policy/core/common/remote_commands/remote_command_job.h
index 897bc3eb..d8343ff 100644
--- a/components/policy/core/common/remote_commands/remote_command_job.h
+++ b/components/policy/core/common/remote_commands/remote_command_job.h
@@ -28,15 +28,22 @@
   using UniqueIDType = int64_t;
 
   // Status of this job.
+  // This enum is used to define the buckets for an enumerated UMA histogram.
+  // Hence,
+  //   (a) existing enumerated constants should never be deleted or reordered
+  //   (b) new constants should only be appended at the end of the enumeration
+  //       (update RemoteCommandExecutuionStatus in
+  //       tools/metrics/histograms/enums.xml as well).
   enum Status {
     NOT_INITIALIZED = 0,  // The job is not initialized yet.
-    INVALID,              // The job was initialized from a malformed protobuf.
-    EXPIRED,              // The job is expired and won't be executed.
-    NOT_STARTED,          // The job is initialized and ready to be started.
-    RUNNING,              // The job was started and is running now.
-    SUCCEEDED,            // The job finished running successfully.
-    FAILED,               // The job finished running with failure.
-    TERMINATED,           // The job was terminated before finishing by itself.
+    INVALID = 1,          // The job was initialized from a malformed protobuf.
+    EXPIRED = 2,          // The job is expired and won't be executed.
+    NOT_STARTED = 3,      // The job is initialized and ready to be started.
+    RUNNING = 4,          // The job was started and is running now.
+    SUCCEEDED = 5,        // The job finished running successfully.
+    FAILED = 6,           // The job finished running with failure.
+    TERMINATED = 7,       // The job was terminated before finishing by itself.
+    STATUS_TYPE_SIZE      // Used by UMA histograms. Shall be the last.
   };
 
   using FinishedCallback = base::OnceClosure;
@@ -91,6 +98,7 @@
   base::TimeTicks issued_time() const { return issued_time_; }
   base::Time execution_started_time() const { return execution_started_time_; }
   Status status() const { return status_; }
+  bool has_signed_data() const { return signed_command_.has_value(); }
 
   // Returns whether execution of this command is finished.
   bool IsExecutionFinished() const;
diff --git a/components/policy/core/common/remote_commands/remote_commands_service.cc b/components/policy/core/common/remote_commands/remote_commands_service.cc
index 41b04f5..0ef2aa16 100644
--- a/components/policy/core/common/remote_commands/remote_commands_service.cc
+++ b/components/policy/core/common/remote_commands/remote_commands_service.cc
@@ -10,23 +10,154 @@
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/metrics/histogram_functions.h"
 #include "base/stl_util.h"
+#include "base/strings/stringprintf.h"
 #include "base/syslog_logging.h"
 #include "components/policy/core/common/cloud/cloud_policy_client.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
 #include "components/policy/core/common/cloud/cloud_policy_store.h"
 #include "components/policy/core/common/cloud/cloud_policy_validator.h"
+#include "components/policy/core/common/cloud/enterprise_metrics.h"
 #include "components/policy/core/common/remote_commands/remote_commands_factory.h"
 
 namespace policy {
 
 namespace em = enterprise_management;
 
+namespace {
+
+RemoteCommandsService::MetricReceivedRemoteCommand RemoteCommandMetricFromType(
+    em::RemoteCommand_Type type) {
+  using Metric = RemoteCommandsService::MetricReceivedRemoteCommand;
+
+  switch (type) {
+    case em::RemoteCommand_Type_COMMAND_ECHO_TEST:
+      return Metric::kCommandEchoTest;
+    case em::RemoteCommand_Type_DEVICE_REBOOT:
+      return Metric::kDeviceReboot;
+    case em::RemoteCommand_Type_DEVICE_SCREENSHOT:
+      return Metric::kDeviceScreenshot;
+    case em::RemoteCommand_Type_DEVICE_SET_VOLUME:
+      return Metric::kDeviceSetVolume;
+    case em::RemoteCommand_Type_DEVICE_START_CRD_SESSION:
+      return Metric::kDeviceStartCrdSession;
+    case em::RemoteCommand_Type_DEVICE_FETCH_STATUS:
+      return Metric::kDeviceFetchStatus;
+    case em::RemoteCommand_Type_USER_ARC_COMMAND:
+      return Metric::kUserArcCommand;
+    case em::RemoteCommand_Type_DEVICE_WIPE_USERS:
+      return Metric::kDeviceWipeUsers;
+    case em::RemoteCommand_Type_DEVICE_REFRESH_ENTERPRISE_MACHINE_CERTIFICATE:
+      return Metric::kDeviceRefreshEnterpriseMachineCertificate;
+    case em::RemoteCommand_Type_DEVICE_REMOTE_POWERWASH:
+      return Metric::kDeviceRemotePowerwash;
+    case em::RemoteCommand_Type_DEVICE_GET_AVAILABLE_DIAGNOSTIC_ROUTINES:
+      return Metric::kDeviceGetAvailableDiagnosticRoutines;
+    case em::RemoteCommand_Type_DEVICE_RUN_DIAGNOSTIC_ROUTINE:
+      return Metric::kDeviceRunDiagnosticRoutine;
+    case em::RemoteCommand_Type_DEVICE_GET_DIAGNOSTIC_ROUTINE_UPDATE:
+      return Metric::kDeviceGetDiagnosticRoutineUpdate;
+  }
+
+  // None of possible types matched. May indicate that there is new unhandled
+  // command type.
+  NOTREACHED() << "Unknown command type to record: " << type;
+  return Metric::kUnknownType;
+}
+
+const char* RemoteCommandTypeToString(em::RemoteCommand_Type type) {
+  switch (type) {
+    case em::RemoteCommand_Type_COMMAND_ECHO_TEST:
+      return "CommandEchoTest";
+    case em::RemoteCommand_Type_DEVICE_REBOOT:
+      return "DeviceReboot";
+    case em::RemoteCommand_Type_DEVICE_SCREENSHOT:
+      return "DeviceScreenshot";
+    case em::RemoteCommand_Type_DEVICE_SET_VOLUME:
+      return "DeviceSetVolume";
+    case em::RemoteCommand_Type_DEVICE_START_CRD_SESSION:
+      return "DeviceStartCrdSession";
+    case em::RemoteCommand_Type_DEVICE_FETCH_STATUS:
+      return "DeviceFetchStatus";
+    case em::RemoteCommand_Type_USER_ARC_COMMAND:
+      return "UserArcCommand";
+    case em::RemoteCommand_Type_DEVICE_WIPE_USERS:
+      return "DeviceWipeUsers";
+    case em::RemoteCommand_Type_DEVICE_REFRESH_ENTERPRISE_MACHINE_CERTIFICATE:
+      return "DeviceRefreshEnterpriseMachineCertificate";
+    case em::RemoteCommand_Type_DEVICE_REMOTE_POWERWASH:
+      return "DeviceRemotePowerwash";
+    case em::RemoteCommand_Type_DEVICE_GET_AVAILABLE_DIAGNOSTIC_ROUTINES:
+      return "DeviceGetAvailableDiagnosticRoutines";
+    case em::RemoteCommand_Type_DEVICE_RUN_DIAGNOSTIC_ROUTINE:
+      return "DeviceRunDiagnosticRoutine";
+    case em::RemoteCommand_Type_DEVICE_GET_DIAGNOSTIC_ROUTINE_UPDATE:
+      return "DeviceGetDiagnosticRoutineUpdate";
+  }
+
+  NOTREACHED() << "Unknown command type: " << type;
+  return "";
+}
+
+}  // namespace
+
+// static
+const char* RemoteCommandsService::GetMetricNameReceivedRemoteCommand(
+    PolicyInvalidationScope scope,
+    bool is_command_signed) {
+  switch (scope) {
+    case PolicyInvalidationScope::kUser:
+      return is_command_signed ? kMetricUserRemoteCommandReceived
+                               : kMetricUserUnsignedRemoteCommandReceived;
+    case PolicyInvalidationScope::kDevice:
+      return is_command_signed ? kMetricDeviceRemoteCommandReceived
+                               : kMetricDeviceUnsignedRemoteCommandReceived;
+    case PolicyInvalidationScope::kDeviceLocalAccount:
+      NOTREACHED() << "Unexpected instance of remote commands service with "
+                      "device local account scope.";
+      return "";
+  }
+}
+
+// static
+std::string RemoteCommandsService::GetMetricNameExecutedRemoteCommand(
+    PolicyInvalidationScope scope,
+    em::RemoteCommand_Type command_type,
+    bool is_command_signed) {
+  const char* base_metric_name = nullptr;
+  switch (scope) {
+    case PolicyInvalidationScope::kUser:
+      base_metric_name = is_command_signed
+                             ? kMetricUserRemoteCommandExecutedTemplate
+                             : kMetricUserUnsignedRemoteCommandExecutedTemplate;
+      break;
+    case PolicyInvalidationScope::kDevice:
+      base_metric_name =
+          is_command_signed
+              ? kMetricDeviceRemoteCommandExecutedTemplate
+              : kMetricDeviceUnsignedRemoteCommandExecutedTemplate;
+      break;
+    case PolicyInvalidationScope::kDeviceLocalAccount:
+      NOTREACHED() << "Unexpected instance of remote commands service with "
+                      "device local account scope.";
+      return "";
+  }
+
+  DCHECK(base_metric_name);
+  return base::StringPrintf(base_metric_name,
+                            RemoteCommandTypeToString(command_type));
+}
+
 RemoteCommandsService::RemoteCommandsService(
     std::unique_ptr<RemoteCommandsFactory> factory,
     CloudPolicyClient* client,
-    CloudPolicyStore* store)
-    : factory_(std::move(factory)), client_(client), store_(store) {
+    CloudPolicyStore* store,
+    PolicyInvalidationScope scope)
+    : factory_(std::move(factory)),
+      client_(client),
+      store_(store),
+      scope_(scope) {
   DCHECK(client_);
   queue_.AddObserver(this);
 }
@@ -95,19 +226,21 @@
       CloudPolicyValidatorBase::SignatureType::SHA1);
 
   auto ignore_result = base::BindOnce(
-      [](std::vector<em::RemoteCommandResult>* unsent_results,
-         const char* error_msg) {
+      [](RemoteCommandsService* self, const char* error_msg,
+         MetricReceivedRemoteCommand metric) {
         SYSLOG(ERROR) << error_msg;
         em::RemoteCommandResult result;
         result.set_result(em::RemoteCommandResult_ResultType_RESULT_IGNORED);
         result.set_command_id(-1);
-        unsent_results->push_back(result);
+        self->unsent_results_.push_back(result);
+        self->RecordReceivedRemoteCommand(metric, /*is_signed=*/true);
       },
-      &unsent_results_);
+      base::Unretained(this));
 
   if (!valid_signature) {
     std::move(ignore_result)
-        .Run("Secure remote command signature verification failed");
+        .Run("Secure remote command signature verification failed",
+             MetricReceivedRemoteCommand::kInvalidSignature);
     return;
   }
 
@@ -117,7 +250,8 @@
       policy_data.policy_type() !=
           dm_protocol::kChromeRemoteCommandPolicyType) {
     std::move(ignore_result)
-        .Run("Secure remote command with wrong PolicyData type");
+        .Run("Secure remote command with wrong PolicyData type",
+             MetricReceivedRemoteCommand::kInvalid);
     return;
   }
 
@@ -125,14 +259,16 @@
   if (!policy_data.has_policy_value() ||
       !command.ParseFromString(policy_data.policy_value())) {
     std::move(ignore_result)
-        .Run("Secure remote command invalid RemoteCommand data");
+        .Run("Secure remote command invalid RemoteCommand data",
+             MetricReceivedRemoteCommand::kInvalid);
     return;
   }
 
   const em::PolicyData* const policy = store_->policy();
   if (!policy || policy->device_id() != command.target_device_id()) {
     std::move(ignore_result)
-        .Run("Secure remote command wrong target device id");
+        .Run("Secure remote command wrong target device id",
+             MetricReceivedRemoteCommand::kInvalid);
     return;
   }
 
@@ -143,14 +279,22 @@
 void RemoteCommandsService::EnqueueCommand(
     const em::RemoteCommand& command,
     const em::SignedData* signed_command) {
+  const bool is_command_signed = signed_command != nullptr;
   if (!command.has_type() || !command.has_command_id()) {
     SYSLOG(ERROR) << "Invalid remote command from server.";
+    const auto metric = !command.has_command_id()
+                            ? MetricReceivedRemoteCommand::kInvalid
+                            : MetricReceivedRemoteCommand::kUnknownType;
+    RecordReceivedRemoteCommand(metric, is_command_signed);
     return;
   }
 
   // If the command is already fetched, ignore it.
-  if (base::Contains(fetched_command_ids_, command.command_id()))
+  if (base::Contains(fetched_command_ids_, command.command_id())) {
+    RecordReceivedRemoteCommand(MetricReceivedRemoteCommand::kDuplicated,
+                                is_command_signed);
     return;
+  }
 
   fetched_command_ids_.push_back(command.command_id());
 
@@ -161,6 +305,10 @@
     SYSLOG(ERROR) << "Initialization of remote command type "
                   << command.type() << " with id " << command.command_id()
                   << " failed.";
+    const auto metric = job == nullptr
+                            ? MetricReceivedRemoteCommand::kInvalidScope
+                            : MetricReceivedRemoteCommand::kInvalid;
+    RecordReceivedRemoteCommand(metric, is_command_signed);
     em::RemoteCommandResult ignored_result;
     ignored_result.set_result(
         em::RemoteCommandResult_ResultType_RESULT_IGNORED);
@@ -169,6 +317,9 @@
     return;
   }
 
+  RecordReceivedRemoteCommand(RemoteCommandMetricFromType(command.type()),
+                              is_command_signed);
+
   queue_.AddJob(std::move(job));
 }
 
@@ -209,6 +360,8 @@
 
   unsent_results_.push_back(result);
 
+  RecordExecutedRemoteCommand(*command);
+
   FetchRemoteCommands();
 }
 
@@ -236,4 +389,20 @@
     FetchRemoteCommands();
 }
 
+void RemoteCommandsService::RecordReceivedRemoteCommand(
+    RemoteCommandsService::MetricReceivedRemoteCommand metric,
+    bool is_command_signed) const {
+  const char* metric_name =
+      GetMetricNameReceivedRemoteCommand(scope_, is_command_signed);
+  base::UmaHistogramEnumeration(metric_name, metric);
+}
+
+void RemoteCommandsService::RecordExecutedRemoteCommand(
+    const RemoteCommandJob& command) const {
+  const std::string metric_name = GetMetricNameExecutedRemoteCommand(
+      scope_, command.GetType(), command.has_signed_data());
+  base::UmaHistogramEnumeration(metric_name, command.status(),
+                                RemoteCommandJob::STATUS_TYPE_SIZE);
+}
+
 }  // namespace policy
diff --git a/components/policy/core/common/remote_commands/remote_commands_service.h b/components/policy/core/common/remote_commands/remote_commands_service.h
index a0f6f68e..b824263 100644
--- a/components/policy/core/common/remote_commands/remote_commands_service.h
+++ b/components/policy/core/common/remote_commands/remote_commands_service.h
@@ -6,6 +6,7 @@
 #define COMPONENTS_POLICY_CORE_COMMON_REMOTE_COMMANDS_REMOTE_COMMANDS_SERVICE_H_
 
 #include <memory>
+#include <string>
 #include <vector>
 
 #include "base/callback_forward.h"
@@ -13,6 +14,7 @@
 #include "base/macros.h"
 #include "base/memory/weak_ptr.h"
 #include "components/policy/core/common/cloud/cloud_policy_constants.h"
+#include "components/policy/core/common/cloud/policy_invalidation_scope.h"
 #include "components/policy/core/common/remote_commands/remote_command_job.h"
 #include "components/policy/core/common/remote_commands/remote_commands_queue.h"
 #include "components/policy/policy_export.h"
@@ -35,9 +37,52 @@
 class POLICY_EXPORT RemoteCommandsService
     : public RemoteCommandsQueue::Observer {
  public:
+  // Represents received remote command status to be recorded.
+  // This enum is used to define the buckets for an enumerated UMA histogram.
+  // Hence,
+  //   (a) existing enumerated constants should never be deleted or reordered
+  //   (b) new constants should only be appended at the end of the enumeration
+  //       (update RemoteCommandReceivedStatus in
+  //       tools/metrics/histograms/enums.xml as well).
+  enum class MetricReceivedRemoteCommand {
+    // Invalid remote commands.
+    kInvalidSignature = 0,
+    kInvalid = 1,
+    kUnknownType = 2,
+    kDuplicated = 3,
+    kInvalidScope = 4,
+    // Remote commands type.
+    kCommandEchoTest = 5,
+    kDeviceReboot = 6,
+    kDeviceScreenshot = 7,
+    kDeviceSetVolume = 8,
+    kDeviceFetchStatus = 9,
+    kUserArcCommand = 10,
+    kDeviceWipeUsers = 11,
+    kDeviceStartCrdSession = 12,
+    kDeviceRemotePowerwash = 13,
+    kDeviceRefreshEnterpriseMachineCertificate = 14,
+    kDeviceGetAvailableDiagnosticRoutines = 15,
+    kDeviceRunDiagnosticRoutine = 16,
+    kDeviceGetDiagnosticRoutineUpdate = 17,
+    // Used by UMA histograms. Shall refer to the last enumeration.
+    kMaxValue = kDeviceGetDiagnosticRoutineUpdate
+  };
+
+  // Returns the metric name to report received commands.
+  static const char* GetMetricNameReceivedRemoteCommand(
+      PolicyInvalidationScope scope,
+      bool is_command_signed);
+  // Returns the metric name to report status of executed commands.
+  static std::string GetMetricNameExecutedRemoteCommand(
+      PolicyInvalidationScope scope,
+      enterprise_management::RemoteCommand_Type command_type,
+      bool is_command_signed);
+
   RemoteCommandsService(std::unique_ptr<RemoteCommandsFactory> factory,
                         CloudPolicyClient* client,
-                        CloudPolicyStore* store);
+                        CloudPolicyStore* store,
+                        PolicyInvalidationScope scope);
   ~RemoteCommandsService() override;
 
   // Attempts to fetch remote commands, mainly supposed to be called by
@@ -83,6 +128,12 @@
       const std::vector<enterprise_management::RemoteCommand>& commands,
       const std::vector<enterprise_management::SignedData>& signed_commands);
 
+  // Records UMA metric of received remote command.
+  void RecordReceivedRemoteCommand(MetricReceivedRemoteCommand metric,
+                                   bool is_command_signed) const;
+  // Records UMA metric of executed remote command.
+  void RecordExecutedRemoteCommand(const RemoteCommandJob& command) const;
+
   // Whether there is a command fetch on going or not.
   bool command_fetch_in_progress_ = false;
 
@@ -118,6 +169,9 @@
   // as executed.
   base::OnceClosure on_command_acked_callback_;
 
+  // Represents remote commands scope covered by service.
+  const PolicyInvalidationScope scope_;
+
   base::WeakPtrFactory<RemoteCommandsService> weak_factory_{this};
 
   DISALLOW_COPY_AND_ASSIGN(RemoteCommandsService);
diff --git a/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc b/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
index 8427529..f59d62fe 100644
--- a/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
+++ b/components/policy/core/common/remote_commands/remote_commands_service_unittest.cc
@@ -15,6 +15,7 @@
 #include "base/macros.h"
 #include "base/memory/ptr_util.h"
 #include "base/run_loop.h"
+#include "base/test/metrics/histogram_tester.h"
 #include "base/test/test_mock_time_task_runner.h"
 #include "base/threading/thread_task_runner_handle.h"
 #include "base/time/tick_clock.h"
@@ -32,6 +33,7 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+using testing::Return;
 using testing::ReturnNew;
 
 namespace policy {
@@ -203,7 +205,8 @@
 };
 
 // Base class for unit tests regarding remote commands service.
-class RemoteCommandsServiceTest : public testing::Test {
+class RemoteCommandsServiceTest
+    : public testing::TestWithParam<PolicyInvalidationScope> {
  protected:
   RemoteCommandsServiceTest()
       : server_(std::make_unique<TestingRemoteCommandsServer>()) {
@@ -215,7 +218,7 @@
 
   void StartService(std::unique_ptr<RemoteCommandsFactory> factory) {
     remote_commands_service_ = std::make_unique<RemoteCommandsService>(
-        std::move(factory), cloud_policy_client_.get(), &store_);
+        std::move(factory), cloud_policy_client_.get(), &store_, GetScope());
     remote_commands_service_->SetClocksForTesting(
         mock_task_runner_->GetMockClock(),
         mock_task_runner_->GetMockTickClock());
@@ -223,6 +226,8 @@
 
   void FlushAllTasks() { mock_task_runner_->FastForwardUntilNoTasksRemain(); }
 
+  PolicyInvalidationScope GetScope() const { return GetParam(); }
+
   const scoped_refptr<base::TestMockTimeTaskRunner> mock_task_runner_ =
       base::MakeRefCounted<base::TestMockTimeTaskRunner>(
           base::TestMockTimeTaskRunner::Type::kBoundToThread);
@@ -238,7 +243,7 @@
 };
 
 // Tests that no command will be fetched if no commands is issued.
-TEST_F(RemoteCommandsServiceTest, NoCommands) {
+TEST_P(RemoteCommandsServiceTest, NoCommands) {
   std::unique_ptr<MockTestRemoteCommandFactory> factory(
       new MockTestRemoteCommandFactory());
   EXPECT_CALL(*factory, BuildTestCommand()).Times(0);
@@ -253,7 +258,7 @@
 }
 
 // Tests that existing commands issued before service started will be fetched.
-TEST_F(RemoteCommandsServiceTest, ExistingCommand) {
+TEST_P(RemoteCommandsServiceTest, ExistingCommand) {
   std::unique_ptr<MockTestRemoteCommandFactory> factory(
       new MockTestRemoteCommandFactory());
   EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
@@ -286,7 +291,7 @@
 }
 
 // Tests that commands issued after service started will be fetched.
-TEST_F(RemoteCommandsServiceTest, NewCommand) {
+TEST_P(RemoteCommandsServiceTest, NewCommand) {
   std::unique_ptr<MockTestRemoteCommandFactory> factory(
       new MockTestRemoteCommandFactory());
   EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
@@ -314,7 +319,7 @@
 
 // Tests that commands issued after service started will be fetched, even if
 // the command is issued when a fetch request is ongoing.
-TEST_F(RemoteCommandsServiceTest, NewCommandFollwingFetch) {
+TEST_P(RemoteCommandsServiceTest, NewCommandFollwingFetch) {
   std::unique_ptr<MockTestRemoteCommandFactory> factory(
       new MockTestRemoteCommandFactory());
   EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
@@ -366,7 +371,7 @@
 
 // Tests that on_command_acked_callback_ gets called after the commands get
 // acked/fetched (one function handles both).
-TEST_F(RemoteCommandsServiceTest, AckedCallback) {
+TEST_P(RemoteCommandsServiceTest, AckedCallback) {
   std::unique_ptr<MockTestRemoteCommandFactory> factory(
       new MockTestRemoteCommandFactory());
   EXPECT_CALL(*factory, BuildTestCommand()).Times(1);
@@ -454,7 +459,7 @@
 };
 
 // Tests that signed remote commands work.
-TEST_F(SignedRemoteCommandsServiceTest, Success) {
+TEST_P(SignedRemoteCommandsServiceTest, Success) {
   ensure_called_.Bind(base::BindOnce(&ExpectSucceededJob, std::string()));
   server_->IssueSignedCommand(
       base::BindOnce(&EnsureCalled::Call, base::Unretained(&ensure_called_)),
@@ -462,7 +467,7 @@
 }
 
 // Tests that we reject signed remote commands with invalid signature.
-TEST_F(SignedRemoteCommandsServiceTest, InvalidSignature) {
+TEST_P(SignedRemoteCommandsServiceTest, InvalidSignature) {
   em::SignedData signed_data;
   signed_data.set_data("some-random-data");
   signed_data.set_signature("random-signature");
@@ -474,7 +479,7 @@
 }
 
 // Tests that we reject signed remote commands with invalid PolicyData type.
-TEST_F(SignedRemoteCommandsServiceTest, InvalidPolicyDataType) {
+TEST_P(SignedRemoteCommandsServiceTest, InvalidPolicyDataType) {
   em::PolicyData policy_data;
   policy_data.set_policy_type("some-random-policy-type");
 
@@ -485,7 +490,7 @@
 }
 
 // Tests that we reject signed remote commands with invalid RemoteCommand data.
-TEST_F(SignedRemoteCommandsServiceTest, InvalidRemoteCommand) {
+TEST_P(SignedRemoteCommandsServiceTest, InvalidRemoteCommand) {
   em::PolicyData policy_data;
   policy_data.set_policy_type("google/chromeos/remotecommand");
 
@@ -496,7 +501,7 @@
 }
 
 // Tests that we reject signed remote commands with invalid target device id.
-TEST_F(SignedRemoteCommandsServiceTest, InvalidDeviceId) {
+TEST_P(SignedRemoteCommandsServiceTest, InvalidDeviceId) {
   em::RemoteCommand remote_command;
   remote_command.set_target_device_id("roadrunner-device");
 
@@ -506,4 +511,374 @@
       &remote_command, nullptr, nullptr);
 }
 
+class RemoteCommandsServiceHistogramTest : public RemoteCommandsServiceTest {
+ protected:
+  using MetricReceivedRemoteCommand =
+      RemoteCommandsService::MetricReceivedRemoteCommand;
+
+  RemoteCommandsServiceHistogramTest() {
+    auto factory = std::make_unique<MockTestRemoteCommandFactory>();
+    factory_ptr = factory.get();
+    StartService(std::move(factory));
+
+    std::vector<uint8_t> public_key = PolicyBuilder::GetPublicTestKey();
+    store_.policy_signature_public_key_.assign(public_key.begin(),
+                                               public_key.end());
+    store_.policy_ = std::make_unique<em::PolicyData>();
+    store_.policy_->set_device_id("acme-device");
+  }
+
+  void ExpectCommand() {
+    cloud_policy_client_->ExpectFetchCommands(
+        FetchCallExpectation().SetFetchedCommands(1));
+    cloud_policy_client_->ExpectFetchCommands(
+        FetchCallExpectation().SetCommandResults(1));
+  }
+
+  void ExpectSignedCommand() {
+    cloud_policy_client_->ExpectFetchCommands(
+        FetchCallExpectation().SetSignedCommands(1));
+    cloud_policy_client_->ExpectFetchCommands(
+        FetchCallExpectation().SetCommandResults(1));
+  }
+
+  std::string GetMetricNameReceived(bool is_command_signed) {
+    return RemoteCommandsService::GetMetricNameReceivedRemoteCommand(
+        GetScope(), is_command_signed);
+  }
+
+  std::string GetMetricNameExecuted(bool is_command_signed) {
+    return RemoteCommandsService::GetMetricNameExecutedRemoteCommand(
+        GetScope(), em::RemoteCommand_Type_COMMAND_ECHO_TEST,
+        is_command_signed);
+  }
+
+  void ExpectReceivedCommands(
+      const std::vector<MetricReceivedRemoteCommand>& metrics,
+      bool is_command_signed) {
+    for (const auto& metric : metrics) {
+      histogram_tester_.ExpectBucketCount(
+          GetMetricNameReceived(is_command_signed), metric, 1);
+    }
+    histogram_tester_.ExpectTotalCount(GetMetricNameReceived(is_command_signed),
+                                       metrics.size());
+  }
+
+  void ExpectExecutedCommands(
+      const std::vector<RemoteCommandJob::Status>& metrics,
+      bool is_command_signed) {
+    for (const auto& metric : metrics) {
+      histogram_tester_.ExpectBucketCount(
+          GetMetricNameExecuted(is_command_signed), metric, 1);
+    }
+    histogram_tester_.ExpectTotalCount(GetMetricNameExecuted(is_command_signed),
+                                       metrics.size());
+  }
+
+  MockTestRemoteCommandFactory* factory_ptr;
+  base::HistogramTester histogram_tester_;
+};
+
+TEST_P(RemoteCommandsServiceHistogramTest, WhenNoCommandsNothingRecorded) {
+  cloud_policy_client_->ExpectFetchCommands(FetchCallExpectation());
+
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  for (const bool is_command_signed : {true, false}) {
+    ExpectReceivedCommands({}, is_command_signed);
+    ExpectExecutedCommands({}, is_command_signed);
+  }
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedUnsignedCommandOfUnknownTypeRecordUnknownType) {
+  cloud_policy_client_->ExpectFetchCommands(
+      FetchCallExpectation().SetFetchedCommands(1));
+  // No result for command without type.
+
+  em::RemoteCommand command;
+  command.set_command_id(123);
+  server_->IssueCommand(command, /*reported_callback=*/{},
+                        /*skip_next_fetch=*/false);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = false;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kUnknownType},
+                         is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedCommandOfUnknownTypeRecordUnknownType) {
+  cloud_policy_client_->ExpectFetchCommands(
+      FetchCallExpectation().SetSignedCommands(1));
+  // No result for command without type.
+
+  em::RemoteCommand command;
+  command.set_target_device_id("acme-device");
+  command.set_command_id(123);
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{}, &command, /*policy_data_in=*/nullptr,
+      /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kUnknownType},
+                         is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedUnsignedCommandWithoutIdRecordInvalid) {
+  cloud_policy_client_->ExpectFetchCommands(
+      FetchCallExpectation().SetFetchedCommands(1));
+  // No result for command without id.
+
+  em::RemoteCommand command;
+  command.set_type(em::RemoteCommand_Type_COMMAND_ECHO_TEST);
+  server_->IssueCommand(command, /*reported_callback=*/{},
+                        /*skip_next_fetch=*/false);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = false;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalid}, is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedCommandWithoutIdRecordInvlaid) {
+  cloud_policy_client_->ExpectFetchCommands(
+      FetchCallExpectation().SetSignedCommands(1));
+  // No result for command without id.
+
+  em::RemoteCommand command;
+  command.set_target_device_id("acme-device");
+  command.set_type(em::RemoteCommand_Type_COMMAND_ECHO_TEST);
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{}, &command, /*policy_data_in=*/nullptr,
+      /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalid}, is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedExistingUnsignedCommandRecordDuplicated) {
+  cloud_policy_client_->ExpectFetchCommands(
+      FetchCallExpectation().SetFetchedCommands(2));
+  cloud_policy_client_->ExpectFetchCommands(
+      FetchCallExpectation().SetCommandResults(1));
+  // No result for command without id.
+  EXPECT_CALL(*factory_ptr, BuildTestCommand()).Times(1);
+
+  server_->IssueCommand(em::RemoteCommand_Type_COMMAND_ECHO_TEST, kTestPayload,
+                        /*reported_callback=*/{},
+                        /*skip_next_fetch=*/false);
+  em::RemoteCommand command;
+  command.set_type(em::RemoteCommand_Type_COMMAND_ECHO_TEST);
+  command.set_command_id(server_->last_command_id());
+  server_->IssueCommand(command, /*reported_callback=*/{},
+                        /*skip_next_fetch=*/false);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = false;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kCommandEchoTest,
+                          MetricReceivedRemoteCommand::kDuplicated},
+                         is_signed);
+  ExpectExecutedCommands({RemoteCommandJob::SUCCEEDED}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedExistingCommandRecordDuplicated) {
+  ExpectSignedCommand();
+  EXPECT_CALL(*factory_ptr, BuildTestCommand()).Times(1);
+
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{},
+      /*command_in=*/nullptr, /*policy_data_in=*/nullptr,
+      /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  cloud_policy_client_->ExpectFetchCommands(
+      FetchCallExpectation().SetSignedCommands(1));
+  // No result for command without id.
+  em::RemoteCommand command;
+  command.set_target_device_id("acme-device");
+  command.set_type(em::RemoteCommand_Type_COMMAND_ECHO_TEST);
+  command.set_command_id(server_->last_command_id());
+  server_->IssueSignedCommand(/*reported_callback=*/{}, &command,
+                              /*policy_data_in=*/nullptr,
+                              /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kCommandEchoTest,
+                          MetricReceivedRemoteCommand::kDuplicated},
+                         is_signed);
+  ExpectExecutedCommands({RemoteCommandJob::SUCCEEDED}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenCannotBuildUnsignedJobRecordInvalidScope) {
+  ExpectCommand();
+  EXPECT_CALL(*factory_ptr, BuildTestCommand()).WillOnce(Return(nullptr));
+
+  server_->IssueCommand(em::RemoteCommand_Type_COMMAND_ECHO_TEST, kTestPayload,
+                        /*reported_callback=*/{},
+                        /*skip_next_fetch=*/false);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = false;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalidScope},
+                         is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenCannotBuildJobRecordInvalidScope) {
+  ExpectSignedCommand();
+  EXPECT_CALL(*factory_ptr, BuildTestCommand()).WillOnce(Return(nullptr));
+
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{},
+      /*command_in=*/nullptr, /*policy_data_in=*/nullptr,
+      /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalidScope},
+                         is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedInvalidSignatureRecordInvalidSignature) {
+  ExpectSignedCommand();
+
+  em::SignedData signed_data;
+  signed_data.set_data("some-random-data");
+  signed_data.set_signature("random-signature");
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{},
+      /*command_in=*/nullptr, /*policy_data_in=*/nullptr, &signed_data);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalidSignature},
+                         is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedInvalidPolicyDataRecordInvalid) {
+  ExpectSignedCommand();
+
+  em::PolicyData policy_data;
+  policy_data.set_policy_type("some-random-policy-type");
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{},
+      /*command_in=*/nullptr, &policy_data, /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalid}, is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedInvalidTargetDeviceRecordInvalid) {
+  ExpectSignedCommand();
+
+  em::RemoteCommand remote_command;
+  remote_command.set_target_device_id("roadrunner-device");
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{}, &remote_command, /*policy_data_in=*/nullptr,
+      /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalid}, is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedInvalidCommandRecordInvalid) {
+  ExpectSignedCommand();
+
+  em::PolicyData policy_data;
+  policy_data.set_policy_type("google/chromeos/remotecommand");
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{},
+      /*command_in=*/nullptr, &policy_data, /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kInvalid}, is_signed);
+  ExpectExecutedCommands({}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest,
+       WhenReceivedValidUnsignedCommandRecordType) {
+  ExpectCommand();
+  EXPECT_CALL(*factory_ptr, BuildTestCommand()).Times(1);
+
+  server_->IssueCommand(em::RemoteCommand_Type_COMMAND_ECHO_TEST, kTestPayload,
+                        /*reported_callback=*/{},
+                        /*skip_next_fetch=*/false);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = false;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kCommandEchoTest},
+                         is_signed);
+  ExpectExecutedCommands({RemoteCommandJob::SUCCEEDED}, is_signed);
+}
+
+TEST_P(RemoteCommandsServiceHistogramTest, WhenReceivedValidCommandRecordType) {
+  ExpectSignedCommand();
+  EXPECT_CALL(*factory_ptr, BuildTestCommand()).Times(1);
+
+  server_->IssueSignedCommand(
+      /*reported_callback=*/{},
+      /*command_in=*/nullptr, /*policy_data_in=*/nullptr,
+      /*signed_data_in=*/nullptr);
+  remote_commands_service_->FetchRemoteCommands();
+  FlushAllTasks();
+
+  constexpr bool is_signed = true;
+  ExpectReceivedCommands({MetricReceivedRemoteCommand::kCommandEchoTest},
+                         is_signed);
+  ExpectExecutedCommands({RemoteCommandJob::SUCCEEDED}, is_signed);
+}
+
+INSTANTIATE_TEST_SUITE_P(RemoteCommandsServiceTestInstance,
+                         RemoteCommandsServiceTest,
+                         testing::Values(PolicyInvalidationScope::kUser,
+                                         PolicyInvalidationScope::kDevice));
+
+INSTANTIATE_TEST_SUITE_P(SignedRemoteCommandsServiceTestInstance,
+                         SignedRemoteCommandsServiceTest,
+                         testing::Values(PolicyInvalidationScope::kUser,
+                                         PolicyInvalidationScope::kDevice));
+
+INSTANTIATE_TEST_SUITE_P(RemoteCommandsServiceHistogramTestInstance,
+                         RemoteCommandsServiceHistogramTest,
+                         testing::Values(PolicyInvalidationScope::kUser,
+                                         PolicyInvalidationScope::kDevice));
 }  // namespace policy
diff --git a/components/policy/core/common/remote_commands/testing_remote_commands_server.cc b/components/policy/core/common/remote_commands/testing_remote_commands_server.cc
index faa15e9..36b2095 100644
--- a/components/policy/core/common/remote_commands/testing_remote_commands_server.cc
+++ b/components/policy/core/common/remote_commands/testing_remote_commands_server.cc
@@ -92,13 +92,19 @@
   if (!payload.empty())
     command.set_payload(payload);
 
-  RemoteCommandWithCallback command_with_callback(
-      command, base::nullopt, clock_->NowTicks(), std::move(reported_callback));
-  if (skip_next_fetch)
-    commands_issued_after_next_fetch_.push_back(
-        std::move(command_with_callback));
-  else
-    commands_.push_back(std::move(command_with_callback));
+  DoIssueCommand(command, /*signed_data=*/base::nullopt,
+                 std::move(reported_callback), skip_next_fetch);
+}
+
+void TestingRemoteCommandsServer::IssueCommand(
+    const em::RemoteCommand& command,
+    ResultReportedCallback reported_callback,
+    bool skip_next_fetch) {
+  DCHECK(thread_checker_.CalledOnValidThread());
+  base::AutoLock auto_lock(lock_);
+
+  DoIssueCommand(command, /*signed_data=*/base::nullopt,
+                 std::move(reported_callback), skip_next_fetch);
 }
 
 void TestingRemoteCommandsServer::IssueSignedCommand(
@@ -135,9 +141,8 @@
     signed_data.set_signature(SignDataWithTestKey(signed_data.data()));
   }
 
-  RemoteCommandWithCallback command_with_callback(
-      command, signed_data, clock_->NowTicks(), std::move(reported_callback));
-  commands_.push_back(std::move(command_with_callback));
+  DoIssueCommand(command, signed_data, std::move(reported_callback),
+                 /*skip_next_fetch=*/false);
 }
 
 void TestingRemoteCommandsServer::FetchCommands(
@@ -226,6 +231,22 @@
   return commands_.size();
 }
 
+void TestingRemoteCommandsServer::DoIssueCommand(
+    const em::RemoteCommand& command,
+    const base::Optional<em::SignedData>& signed_data,
+    ResultReportedCallback reported_callback,
+    bool skip_next_fetch) {
+  RemoteCommandWithCallback command_with_callback(
+      command, signed_data, clock_->NowTicks(), std::move(reported_callback));
+
+  if (skip_next_fetch) {
+    commands_issued_after_next_fetch_.push_back(
+        std::move(command_with_callback));
+  } else {
+    commands_.push_back(std::move(command_with_callback));
+  }
+}
+
 void TestingRemoteCommandsServer::ReportJobResult(
     ResultReportedCallback reported_callback,
     const em::RemoteCommandResult& job_result) const {
diff --git a/components/policy/core/common/remote_commands/testing_remote_commands_server.h b/components/policy/core/common/remote_commands/testing_remote_commands_server.h
index ce0bbe3d..34c3a08 100644
--- a/components/policy/core/common/remote_commands/testing_remote_commands_server.h
+++ b/components/policy/core/common/remote_commands/testing_remote_commands_server.h
@@ -64,6 +64,9 @@
                     const std::string& payload,
                     ResultReportedCallback reported_callback,
                     bool skip_next_fetch);
+  void IssueCommand(const enterprise_management::RemoteCommand& command,
+                    ResultReportedCallback reported_callback,
+                    bool skip_next_fetch);
   void IssueSignedCommand(ResultReportedCallback reported_callback,
                           enterprise_management::RemoteCommand* command_in,
                           enterprise_management::PolicyData* policy_data_in,
@@ -91,9 +94,19 @@
   // This number also includes commands which have not been fetched yet.
   size_t NumberOfCommandsPendingResult() const;
 
+  // The latest command ID generated by server.
+  RemoteCommandJob::UniqueIDType last_command_id() const {
+    return last_generated_unique_id_;
+  }
+
  private:
   struct RemoteCommandWithCallback;
 
+  void DoIssueCommand(
+      const enterprise_management::RemoteCommand& command,
+      const base::Optional<enterprise_management::SignedData>& signed_data,
+      ResultReportedCallback reported_callback,
+      bool skip_next_fetch);
   void ReportJobResult(
       ResultReportedCallback reported_callback,
       const enterprise_management::RemoteCommandResult& job_result) const;
diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto
index e7bd1963..e40655d 100644
--- a/components/policy/proto/device_management_backend.proto
+++ b/components/policy/proto/device_management_backend.proto
@@ -2135,6 +2135,11 @@
 // This protobuf defines a single remote command from server to client for
 // execution.
 message RemoteCommand {
+  // The names are used as part of metric names. If enumeration is updated
+  // the names should also be updated:
+  // - components/policy/core/common/cloud/enterprise_metrics.cc;
+  // - components/policy/core/common/remote_commands/remote_commands_service.cc;
+  // - Enterprise.RemoteCommandType in tools/metrics/histograms/histograms.xml;
   enum Type {
     // Simple echo command for testing, will be ignored in production code.
     COMMAND_ECHO_TEST = -1;
@@ -2175,6 +2180,8 @@
 
     // Send a command or get an update from an existing diagnostics routine.
     DEVICE_GET_DIAGNOSTIC_ROUTINE_UPDATE = 11;
+
+    // Please update metrics after adding a new item - see the comment above.
   }
 
   // The command type.
diff --git a/components/policy/resources/policy_templates.gni b/components/policy/resources/policy_templates.gni
index acb9f6e..7a45452 100644
--- a/components/policy/resources/policy_templates.gni
+++ b/components/policy/resources/policy_templates.gni
@@ -101,7 +101,11 @@
     "$policy_templates_base_dir/mac/app-Manifest.plist"
 policy_templates_plist_strings_path =
     "$policy_templates_base_dir/mac/strings/\${lang}.lproj/Localizable.strings"
-policy_templates_mac_outputs = [ policy_templates_plist_path ]
+policy_templates_jamf_path = "$policy_templates_base_dir/mac/jamf.json"
+policy_templates_mac_outputs = [
+  policy_templates_plist_path,
+  policy_templates_jamf_path,
+]
 foreach(lang, mac_policy_templates_languages) {
   policy_templates_mac_outputs += [
     "$policy_templates_base_dir/mac/strings/${lang}.lproj/Localizable.strings",
diff --git a/components/policy/resources/policy_templates.json b/components/policy/resources/policy_templates.json
index c9d3b0f..7e16482 100644
--- a/components/policy/resources/policy_templates.json
+++ b/components/policy/resources/policy_templates.json
@@ -12435,7 +12435,7 @@
 
       If the policy is set to "Enable browser sign-in," then the user is allowed to sign in to the browser and is automatically signed in to the browser when signed in to Google web services like Gmail. Being signed in to the browser means the user's account information will be kept by the browser. However, it does not mean that Chrome sync will be turned on per default; the user must separately opt-in to use this feature. Enabling this policy will prevent the user from turning off the setting that allows browser sign-in. To control the availability of Chrome sync, use the "SyncDisabled" policy.
 
-      If the policy is set to "Force browser sign-in" the user is presented with an account selection dialog and has to choose and sign in to an account to use the browser. This ensures that for managed accounts the policies associated with the account are applied and enforced. By default this turns on Chrome sync for the account, except for the case when sync was disabled by the domain admin or via the "SyncDisabled" policy. The default value of BrowserGuestModeEnabled will be set to false. Note that existing unsigned profiles will be locked and inaccessible after enabling this policy. For more information, see help center article: https://support.google.com/chrome/a/answer/7572556. This option does not support Linux and will fallback to "Enable browser sign-in" if used.
+      If the policy is set to "Force browser sign-in" the user is presented with an account selection dialog and has to choose and sign in to an account to use the browser. This ensures that for managed accounts the policies associated with the account are applied and enforced. By default this turns on Chrome sync for the account, except for the case when sync was disabled by the domain admin or via the "SyncDisabled" policy. The default value of BrowserGuestModeEnabled will be set to false. Note that existing unsigned profiles will be locked and inaccessible after enabling this policy. For more information, see help center article: https://support.google.com/chrome/a/answer/7572556. This option does not support Linux and Android, it will fallback to "Enable browser sign-in" if used.
 
       If this policy is not set then the user can decide if they want to enable the browser sign in option and use it as they see fit.''',
     },
diff --git a/components/policy/tools/template_writers/template_formatter.py b/components/policy/tools/template_writers/template_formatter.py
index e4941bf..56b63c5 100755
--- a/components/policy/tools/template_writers/template_formatter.py
+++ b/components/policy/tools/template_writers/template_formatter.py
@@ -22,7 +22,7 @@
                     google_admx_writer, google_adml_writer, \
                     android_policy_writer, reg_writer, doc_writer, \
                     doc_atomic_groups_writer , json_writer, plist_writer, \
-                    plist_strings_writer, ios_app_config_writer
+                    plist_strings_writer, ios_app_config_writer, jamf_writer
 
 
 def MacLanguageMap(lang):
@@ -65,6 +65,7 @@
     WriterDesc('json', False, 'utf-8', None, False),
     WriterDesc('plist', False, 'utf-8', None, False),
     WriterDesc('plist_strings', True, 'utf-8', MacLanguageMap, False),
+    WriterDesc('jamf', False, 'utf-8', None, False),
     WriterDesc('ios_app_config', False, 'utf-8', None, False),
 ]
 
@@ -184,6 +185,7 @@
   parser.add_argument('--json', action='append', dest='json')
   parser.add_argument('--plist', action='append', dest='plist')
   parser.add_argument('--plist_strings', action='append', dest='plist_strings')
+  parser.add_argument('--jamf', action='append', dest='jamf')
   parser.add_argument(
       '--android_policy', action='append', dest='android_policy')
   parser.add_argument(
diff --git a/components/policy/tools/template_writers/test_suite_all.py b/components/policy/tools/template_writers/test_suite_all.py
index 933582a6..87b2317 100755
--- a/components/policy/tools/template_writers/test_suite_all.py
+++ b/components/policy/tools/template_writers/test_suite_all.py
@@ -26,6 +26,7 @@
     import writers.google_adml_writer_unittest
     import writers.google_admx_writer_unittest
     import writers.ios_app_config_writer_unittest
+    import writers.jamf_writer_unittest
     import writers.json_writer_unittest
     import writers.plist_strings_writer_unittest
     import writers.plist_writer_unittest
@@ -45,6 +46,7 @@
         writers.google_adml_writer_unittest.GoogleAdmlWriterUnittest,
         writers.google_admx_writer_unittest.GoogleAdmxWriterUnittest,
         writers.ios_app_config_writer_unittest.IOSAppConfigWriterUnitTests,
+        writers.jamf_writer_unittest.JamfWriterUnitTests,
         writers.json_writer_unittest.JsonWriterUnittest,
         writers.plist_strings_writer_unittest.PListStringsWriterUnittest,
         writers.plist_writer_unittest.PListWriterUnittest,
diff --git a/components/policy/tools/template_writers/writer_configuration.py b/components/policy/tools/template_writers/writer_configuration.py
index 87b98020..f67ebeb 100755
--- a/components/policy/tools/template_writers/writer_configuration.py
+++ b/components/policy/tools/template_writers/writer_configuration.py
@@ -54,8 +54,7 @@
                 'mandatory_category_path': ['chromium_os'],
                 'recommended_category_path': ['chromium_os_recommended'],
                 'category_path_strings': {
-                    'chromium_os':
-                        'Chromium OS',
+                    'chromium_os': 'Chromium OS',
                     'chromium_os_recommended':
                     'Chromium OS - {doc_recommended}',
                 },
@@ -65,7 +64,7 @@
         },
         'admx_prefix': 'chromium',
         'linux_policy_path': '/etc/chromium/policies/',
-        'ios_bundle_id': 'org.chromium',
+        'bundle_id': 'org.chromium',
     }
   elif '_google_chrome' in defines:
     config = {
@@ -80,15 +79,12 @@
                 'Software\\Policies\\Google\\Chrome',
                 'reg_recommended_key_name':
                 'Software\\Policies\\Google\\Chrome\\Recommended',
-                'mandatory_category_path': [
-                    'Google:Cat_Google', 'googlechrome'
-                ],
-                'recommended_category_path': [
-                    'Google:Cat_Google', 'googlechrome_recommended'
-                ],
+                'mandatory_category_path':
+                ['Google:Cat_Google', 'googlechrome'],
+                'recommended_category_path':
+                ['Google:Cat_Google', 'googlechrome_recommended'],
                 'category_path_strings': {
-                    'googlechrome':
-                        'Google Chrome',
+                    'googlechrome': 'Google Chrome',
                     'googlechrome_recommended':
                     'Google Chrome - {doc_recommended}'
                 },
@@ -100,12 +96,10 @@
                 'Software\\Policies\\Google\\ChromeOS',
                 'reg_recommended_key_name':
                 'Software\\Policies\\Google\\ChromeOS\\Recommended',
-                'mandatory_category_path': [
-                    'Google:Cat_Google', 'googlechromeos'
-                ],
-                'recommended_category_path': [
-                    'Google:Cat_Google', 'googlechromeos_recommended'
-                ],
+                'mandatory_category_path':
+                ['Google:Cat_Google', 'googlechromeos'],
+                'recommended_category_path':
+                ['Google:Cat_Google', 'googlechromeos_recommended'],
                 'category_path_strings': {
                     'googlechromeos':
                     'Google Chrome OS',
@@ -126,7 +120,7 @@
             'Google': 'Google.Policies'  # prefix: namespace
         },
         'linux_policy_path': '/etc/opt/chrome/policies/',
-        'ios_bundle_id': 'com.google.chrome',
+        'bundle_id': 'com.google.chrome',
     }
   else:
     raise Exception('Unknown build')
diff --git a/components/policy/tools/template_writers/writers/ios_app_config_writer.py b/components/policy/tools/template_writers/writers/ios_app_config_writer.py
index 2b4296e..e280a80 100755
--- a/components/policy/tools/template_writers/writers/ios_app_config_writer.py
+++ b/components/policy/tools/template_writers/writers/ios_app_config_writer.py
@@ -27,8 +27,7 @@
   def BeginTemplate(self):
     self._app_config.attributes[
         'xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'
-    schema_location = '/%s/appconfig/appconfig.xsd' % (
-        self.config['ios_bundle_id'])
+    schema_location = '/%s/appconfig/appconfig.xsd' % (self.config['bundle_id'])
     self._app_config.attributes[
         'xsi:noNamespaceSchemaLocation'] = schema_location
 
@@ -36,12 +35,13 @@
     self.AddText(version, self.config['version'])
 
     bundle_id = self.AddElement(self._app_config, 'bundleId', {})
-    self.AddText(bundle_id, self.config['ios_bundle_id'])
+    self.AddText(bundle_id, self.config['bundle_id'])
     self._policies = self.AddElement(self._app_config, 'dict', {})
 
   def WritePolicy(self, policy):
     element_type = self.policy_type_to_xml_tag[policy['type']]
-    self.AddElement(self._policies, element_type, {'keyName': policy['name']})
+    if element_type:
+      self.AddElement(self._policies, element_type, {'keyName': policy['name']})
 
   def Init(self):
     self._doc = self.CreateDocument()
@@ -54,6 +54,7 @@
         'string-enum-list': 'stringArray',
         'main': 'boolean',
         'list': 'stringArray',
+        'dict': None,
     }
 
   def GetTemplateText(self):
diff --git a/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py b/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
index b5bb4a8c..71e85c1 100755
--- a/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
+++ b/components/policy/tools/template_writers/writers/ios_app_config_writer_unittest.py
@@ -40,14 +40,17 @@
 ''' % (policy_name, policy_type)
 
   def _GetExpectedOutput(self, version, tag):
+    if tag:
+      policies = '<dict>\n    %s\n  </dict>' % tag
+    else:
+      policies = '<dict/>'
+
     return '''<?xml version="1.0" ?>
 <managedAppConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/com.google.chrome/appconfig/appconfig.xsd">
   <version>%s</version>
   <bundleId>com.google.chrome</bundleId>
-  <dict>
-    %s
-  </dict>
-</managedAppConfiguration>''' % (version, tag)
+  %s
+</managedAppConfiguration>''' % (version, policies)
 
   def testStringPolicy(self):
     policy_json = self._GetTestPolicyTemplate('StringPolicy', 'string')
@@ -120,6 +123,17 @@
     }, 'ios_app_config')
     self.assertEquals(output.strip(), expected.strip())
 
+  def testDictPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('DictPolicy', 'dict')
+    # Dict policies are not supported by the appconfig.xml format and should not
+    # be present in the output.
+    expected = self._GetExpectedOutput('83.0.4089.0', None)
+    output = self.GetOutput(policy_json, {
+        '_google_chrome': '1',
+        'version': '83.0.4089.0'
+    }, 'ios_app_config')
+    self.assertEquals(output.strip(), expected.strip())
+
 
 if __name__ == '__main__':
   unittest.main()
diff --git a/components/policy/tools/template_writers/writers/jamf_writer.py b/components/policy/tools/template_writers/writers/jamf_writer.py
new file mode 100755
index 0000000..efef93a7
--- /dev/null
+++ b/components/policy/tools/template_writers/writers/jamf_writer.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import json
+
+from writers import template_writer
+
+
+def GetWriter(config):
+  '''Factory method for creating JamfWriter objects.
+  See the constructor of TemplateWriter for description of
+  arguments.
+  '''
+  return JamfWriter(['mac'], config)
+
+
+class JamfWriter(template_writer.TemplateWriter):
+  '''Simple writer that writes a jamf.json file.
+  '''
+
+  TYPE_TO_INPUT = {
+      'string': 'string',
+      'int': 'integer',
+      'int-enum': 'integer',
+      'string-enum': 'string',
+      'string-enum-list': 'array',
+      'main': 'boolean',
+      'list': 'array',
+      'dict': 'dictionary',
+      'external': 'dictionary',
+  }
+
+  def WriteTemplate(self, template):
+    '''Writes the given template definition.
+
+    Args:
+      template: Template definition to write.
+
+    Returns:
+      Generated output for the passed template definition.
+    '''
+    policies = [
+        policy for policy in template['policy_definitions']
+        if policy['type'] != 'group' and self.IsPolicySupported(policy)
+    ]
+    output = {
+        'title': self.config['bundle_id'],
+        'description': self.config['app_name'],
+        'options': {
+            'remove_empty_properties': True
+        },
+        'properties': {}
+    }
+
+    for policy in policies:
+      output['properties'][policy['name']] = {
+          'title': policy['name'],
+          'description': policy['caption'],
+          'type': self.TYPE_TO_INPUT[policy['type']]
+      }
+
+    return json.dumps(output, indent=2, sort_keys=True, separators=(',', ': '))
diff --git a/components/policy/tools/template_writers/writers/jamf_writer_unittest.py b/components/policy/tools/template_writers/writers/jamf_writer_unittest.py
new file mode 100755
index 0000000..66ffc4cd8
--- /dev/null
+++ b/components/policy/tools/template_writers/writers/jamf_writer_unittest.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import os
+import sys
+if __name__ == '__main__':
+  sys.path.append(os.path.join(os.path.dirname(__file__), '../../../..'))
+
+import unittest
+
+from writers import writer_unittest_common
+
+
+class JamfWriterUnitTests(writer_unittest_common.WriterUnittestCommon):
+  '''Unit tests for JamfWriter.'''
+
+  def _GetTestPolicyTemplate(self, policy_name, policy_type, policy_captio):
+    return '''
+{
+  'policy_definitions': [
+    {
+      'name': '%s',
+      'type': '%s',
+      'supported_on':['chrome.mac:*-'],
+      'caption': '%s',
+      'desc': '',
+      'items': [{
+        'name': '',
+          'value': 1,
+          'caption': '',
+        }]
+    },
+  ],
+  'policy_atomic_group_definitions': [],
+  'placeholders': [],
+  'messages': {},
+}
+''' % (policy_name, policy_type, policy_captio)
+
+  def _GetExpectedOutput(self, policy_name, policy_type, policy_captio):
+    return '''{
+  "description": "Google Chrome",
+  "options": {
+    "remove_empty_properties": true
+  },
+  "properties": {
+    "%s": {
+      "description": "%s",
+      "title": "%s",
+      "type": "%s"
+    }
+  },
+  "title": "com.google.chrome"
+}''' % (policy_name, policy_captio, policy_name, policy_type)
+
+  def testStringPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('stringPolicy', 'string',
+                                              'A string policy')
+    expected = self._GetExpectedOutput('stringPolicy', 'string',
+                                       'A string policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testIntPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('intPolicy', 'int',
+                                              'An int policy')
+    expected = self._GetExpectedOutput('intPolicy', 'integer', 'An int policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testIntEnumPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('intPolicy', 'int-enum',
+                                              'An int-enum policy')
+    expected = self._GetExpectedOutput('intPolicy', 'integer',
+                                       'An int-enum policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testStringEnumPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('stringPolicy', 'string-enum',
+                                              'A string-enum policy')
+    expected = self._GetExpectedOutput('stringPolicy', 'string',
+                                       'A string-enum policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testStringEnumListPolicy(self):
+    policy_json = self._GetTestPolicyTemplate(
+        'stringPolicy', 'string-enum-list', 'A string-enum-list policy')
+    expected = self._GetExpectedOutput('stringPolicy', 'array',
+                                       'A string-enum-list policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testBooleanPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('booleanPolicy', 'main',
+                                              'A boolean policy')
+    expected = self._GetExpectedOutput('booleanPolicy', 'boolean',
+                                       'A boolean policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testListPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('listPolicy', 'list',
+                                              'A list policy')
+    expected = self._GetExpectedOutput('listPolicy', 'array', 'A list policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testDictPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('dictPolicy', 'dict',
+                                              'A dict policy')
+    expected = self._GetExpectedOutput('dictPolicy', 'dictionary',
+                                       'A dict policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+  def testExternalPolicy(self):
+    policy_json = self._GetTestPolicyTemplate('externalPolicy', 'external',
+                                              'A external policy')
+    expected = self._GetExpectedOutput('externalPolicy', 'dictionary',
+                                       'A external policy')
+    output = self.GetOutput(policy_json, {'_google_chrome': '1'}, 'jamf')
+    self.assertEquals(output.strip(), expected.strip())
+
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc b/components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc
index 268285f..e31664f 100644
--- a/components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc
+++ b/components/safe_browsing/core/db/v4_get_hash_protocol_manager.cc
@@ -304,9 +304,7 @@
   // TODO(crbug.com/1028755): Enable full hash checks on iOS.
 #if defined(OS_IOS)
   std::move(callback).Run(cached_full_hash_infos);
-  return;
-#endif
-
+#else
   net::NetworkTrafficAnnotationTag traffic_annotation =
       net::DefineNetworkTrafficAnnotation("safe_browsing_v4_get_hash", R"(
         semantics {
@@ -361,6 +359,7 @@
       clock_->Now()));
   UMA_HISTOGRAM_COUNTS_100("SafeBrowsing.V4GetHash.CountOfPrefixes",
                            prefixes_to_request.size());
+#endif
 }
 
 void V4GetHashProtocolManager::GetFullHashesWithApis(
diff --git a/components/sync/base/sync_base_switches.cc b/components/sync/base/sync_base_switches.cc
index b525652..e5992d6 100644
--- a/components/sync/base/sync_base_switches.cc
+++ b/components/sync/base/sync_base_switches.cc
@@ -6,6 +6,10 @@
 
 namespace switches {
 
+const base::Feature kSyncNigoriRemoveMetadataOnCacheGuidMismatch{
+    "SyncNigoriRemoveMetadataOnCacheGuidMismatch",
+    base::FEATURE_ENABLED_BY_DEFAULT};
+
 // Force disables scrypt key derivation for custom passphrase. If this feature
 // is enabled, scrypt will be considered as an unsupported method, and Chrome
 // will not be able to access data encrypted using scrypt-derived keys (valid
diff --git a/components/sync/base/sync_base_switches.h b/components/sync/base/sync_base_switches.h
index c7624dfe..ff4efb9 100644
--- a/components/sync/base/sync_base_switches.h
+++ b/components/sync/base/sync_base_switches.h
@@ -10,6 +10,7 @@
 
 namespace switches {
 
+extern const base::Feature kSyncNigoriRemoveMetadataOnCacheGuidMismatch;
 extern const base::Feature kSyncForceDisableScryptForCustomPassphrase;
 extern const base::Feature kSyncE2ELatencyMeasurement;
 extern const base::Feature kSyncCustomSharingMessageNudgeDelay;
diff --git a/components/sync/nigori/nigori_model_type_processor.cc b/components/sync/nigori/nigori_model_type_processor.cc
index db55247..3c5a0cf 100644
--- a/components/sync/nigori/nigori_model_type_processor.cc
+++ b/components/sync/nigori/nigori_model_type_processor.cc
@@ -8,6 +8,7 @@
 #include "base/threading/sequenced_task_runner_handle.h"
 #include "components/sync/base/client_tag_hash.h"
 #include "components/sync/base/data_type_histogram.h"
+#include "components/sync/base/sync_base_switches.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/commit_queue.h"
 #include "components/sync/engine_impl/conflict_resolver.h"
@@ -417,13 +418,24 @@
     return;
   }
 
-  if (model_type_state_.initial_sync_done() &&
-      model_type_state_.cache_guid() != activation_request_.cache_guid) {
-    ClearMetadataAndReset();
-    DCHECK(model_ready_to_sync_);
-  }
+  if (base::FeatureList::IsEnabled(
+          switches::kSyncNigoriRemoveMetadataOnCacheGuidMismatch)) {
+    if (model_type_state_.initial_sync_done() &&
+        model_type_state_.cache_guid() != activation_request_.cache_guid) {
+      ClearMetadataAndReset();
+      DCHECK(model_ready_to_sync_);
+    }
 
-  model_type_state_.set_cache_guid(activation_request_.cache_guid);
+    model_type_state_.set_cache_guid(activation_request_.cache_guid);
+  } else {
+    // Legacy logic.
+    if (!model_type_state_.has_cache_guid()) {
+      model_type_state_.set_cache_guid(activation_request_.cache_guid);
+    } else if (model_type_state_.cache_guid() !=
+               activation_request_.cache_guid) {
+      // Not implemented in legacy codepath.
+    }
+  }
 
   // Cache GUID verification earlier above guarantees the user is the same.
   model_type_state_.set_authenticated_account_id(
diff --git a/components/sync/nigori/nigori_model_type_processor_unittest.cc b/components/sync/nigori/nigori_model_type_processor_unittest.cc
index 54140fd..cb4bc17 100644
--- a/components/sync/nigori/nigori_model_type_processor_unittest.cc
+++ b/components/sync/nigori/nigori_model_type_processor_unittest.cc
@@ -10,7 +10,9 @@
 #include "base/bind.h"
 #include "base/bind_helpers.h"
 #include "base/test/mock_callback.h"
+#include "base/test/scoped_feature_list.h"
 #include "components/sync/base/client_tag_hash.h"
+#include "components/sync/base/sync_base_switches.h"
 #include "components/sync/base/time.h"
 #include "components/sync/engine/commit_queue.h"
 #include "components/sync/nigori/nigori_sync_bridge.h"
@@ -546,6 +548,32 @@
                                 std::move(updates));
 }
 
+TEST_F(NigoriModelTypeProcessorTest,
+       ShouldNotResetDataOnCacheGuidMismatchWhenDisabled) {
+  base::test::ScopedFeatureList features;
+  features.InitAndDisableFeature(
+      switches::kSyncNigoriRemoveMetadataOnCacheGuidMismatch);
+
+  SimulateModelReadyToSync(/*initial_sync_done=*/true);
+  ASSERT_TRUE(ProcessorHasEntity());
+
+  syncer::DataTypeActivationRequest request;
+  request.error_handler = base::DoNothing();
+  const char kOtherCacheGuid[] = "OtherCacheGuid";
+  request.cache_guid = kOtherCacheGuid;
+  ASSERT_NE(processor()->GetMetadata().model_type_state.cache_guid(),
+            kOtherCacheGuid);
+  ASSERT_TRUE(processor()->IsTrackingMetadata());
+
+  EXPECT_CALL(*mock_nigori_sync_bridge(), ApplyDisableSyncChanges()).Times(0);
+  processor()->OnSyncStarting(request, base::DoNothing());
+
+  EXPECT_TRUE(processor()->IsTrackingMetadata());
+  EXPECT_EQ(processor()->GetModelTypeStateForTest().cache_guid(), kCacheGuid);
+
+  EXPECT_TRUE(ProcessorHasEntity());
+}
+
 TEST_F(NigoriModelTypeProcessorTest, ShouldDisconnectWhenMergeSyncDataFails) {
   SimulateModelReadyToSync(/*initial_sync_done=*/false);
 
diff --git a/content/browser/accessibility/browser_accessibility_unittest.cc b/content/browser/accessibility/browser_accessibility_unittest.cc
index 4285700..163a967c 100644
--- a/content/browser/accessibility/browser_accessibility_unittest.cc
+++ b/content/browser/accessibility/browser_accessibility_unittest.cc
@@ -743,14 +743,20 @@
   auto position = input_accessible->CreatePositionAt(
       0, ax::mojom::TextAffinity::kDownstream);
 
-  // On platforms that expose IA2 or ATK hypertext, moving by word should have
-  // no effect, i.e. return the same position, since the visible text is just a
-  // placeholder and should not appear in the input field's hypertext.
+  // On platforms that expose IA2 or ATK hypertext, moving by word should work
+  // the same as if the value of the text field is equal to the placeholder
+  // text.
+  //
+  // This is because visually the placeholder text appears in the text field in
+  // the same location as its value, and the user should be able to read it
+  // using standard screen reader commands, such as "read current word" and
+  // "read current line". Only once the user starts typing should the
+  // placeholder disappear.
+
   auto next_word_start = position->CreateNextWordStartPosition(
       ui::AXBoundaryBehavior::CrossBoundary);
-  ASSERT_TRUE(next_word_start->IsTextPosition());
   if (position->MaxTextOffset() == 0) {
-    EXPECT_EQ(*position, *next_word_start);
+    EXPECT_TRUE(next_word_start->IsNullPosition());
   } else {
     EXPECT_EQ(
         "TextPosition anchor_id=2 text_offset=7 affinity=downstream "
@@ -760,9 +766,8 @@
 
   auto next_word_end = position->CreateNextWordEndPosition(
       ui::AXBoundaryBehavior::CrossBoundary);
-  ASSERT_TRUE(next_word_end->IsTextPosition());
   if (position->MaxTextOffset() == 0) {
-    EXPECT_EQ(*position, *next_word_end);
+    EXPECT_TRUE(next_word_end->IsNullPosition());
   } else {
     EXPECT_EQ(
         "TextPosition anchor_id=2 text_offset=6 affinity=downstream "
diff --git a/content/browser/cache_storage/cache_storage_context_impl.cc b/content/browser/cache_storage/cache_storage_context_impl.cc
index 1093d52e..9eadfaa9 100644
--- a/content/browser/cache_storage/cache_storage_context_impl.cc
+++ b/content/browser/cache_storage/cache_storage_context_impl.cc
@@ -11,6 +11,7 @@
 #include "base/task/post_task.h"
 #include "base/task/thread_pool.h"
 #include "base/threading/thread_task_runner_handle.h"
+#include "build/build_config.h"
 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
 #include "content/browser/cache_storage/cache_storage_dispatcher_host.h"
 #include "content/browser/cache_storage/cache_storage_quota_client.h"
@@ -28,8 +29,18 @@
 
 namespace {
 
+// TODO(crbug/960012): Disabled on chromeos for now due to performance
+// regressions that need to be investigated.
+// TODO(crbug/960012): Disabled on fuchsia for test failures.
 const base::Feature kCacheStorageSequenceFeature{
-    "CacheStorageSequence", base::FEATURE_DISABLED_BY_DEFAULT};
+  "CacheStorageSequence",
+#if defined(OS_CHROMEOS) || defined(OS_FUCHSIA)
+      base::FEATURE_DISABLED_BY_DEFAULT
+};
+#else
+      base::FEATURE_ENABLED_BY_DEFAULT
+};
+#endif
 
 scoped_refptr<base::SequencedTaskRunner> CreateSchedulerTaskRunner() {
   if (!base::FeatureList::IsEnabled(kCacheStorageSequenceFeature))
diff --git a/content/browser/renderer_host/render_widget_host_view_android.cc b/content/browser/renderer_host/render_widget_host_view_android.cc
index 9f0eafe..019633c 100644
--- a/content/browser/renderer_host/render_widget_host_view_android.cc
+++ b/content/browser/renderer_host/render_widget_host_view_android.cc
@@ -441,10 +441,6 @@
                                           top_shown_pix);
   }
 
-  auto* wcax = GetWebContentsAccessibilityAndroid();
-  if (wcax)
-    wcax->UpdateFrameInfo(metadata.page_scale_factor);
-
   if (!gesture_listener_manager_)
     return;
 
@@ -482,6 +478,11 @@
       metadata.min_page_scale_factor, metadata.max_page_scale_factor,
       root_layer_size_dip, scrollable_viewport_size_dip, top_content_offset_dip,
       top_shown_pix, controls_changed);
+  // This needs to be called after GestureListenerManager::UpdateScrollInfo, as
+  // it depends on frame info being updated during the UpdateScrollInfo call.
+  auto* wcax = GetWebContentsAccessibilityAndroid();
+  if (wcax)
+    wcax->UpdateFrameInfo(metadata.page_scale_factor);
 
   page_scale_ = metadata.page_scale_factor;
   min_page_scale_ = metadata.min_page_scale_factor;
diff --git a/content/browser/storage_service_restart_browsertest.cc b/content/browser/storage_service_restart_browsertest.cc
index e3529b54..de207ff 100644
--- a/content/browser/storage_service_restart_browsertest.cc
+++ b/content/browser/storage_service_restart_browsertest.cc
@@ -122,7 +122,14 @@
                          R"(getSessionStorageValue("foo"))"));
 }
 
-IN_PROC_BROWSER_TEST_F(StorageServiceRestartBrowserTest, LocalStorageRecovery) {
+// Flakey on Linux, see crbug.com/1066138
+#if defined(OS_LINUX)
+#define MAYBE_LocalStorageRecovery DISABLED_LocalStorageRecovery
+#else
+#define MAYBE_LocalStorageRecovery LocalStorageRecovery
+#endif
+IN_PROC_BROWSER_TEST_F(StorageServiceRestartBrowserTest,
+                       MAYBE_LocalStorageRecovery) {
   // Tests that the Local Storage API can recover and continue normal operation
   // after a Storage Service crash.
   EXPECT_TRUE(
diff --git a/content/browser/webauth/authenticator_impl_unittest.cc b/content/browser/webauth/authenticator_impl_unittest.cc
index 1f201ecf..80520d6 100644
--- a/content/browser/webauth/authenticator_impl_unittest.cc
+++ b/content/browser/webauth/authenticator_impl_unittest.cc
@@ -5019,31 +5019,26 @@
     content::RenderViewHostTestHarness::NavigateAndCommit(url);
   }
 
-  mojo::Remote<blink::mojom::InternalAuthenticator> ConnectToAuthenticator(
-      GURL effective_origin_url) {
-    internal_authenticator_impl_ = std::make_unique<InternalAuthenticatorImpl>(
-        main_rfh(), url::Origin::Create(effective_origin_url));
-    mojo::Remote<blink::mojom::InternalAuthenticator> authenticator;
-    internal_authenticator_impl_->Bind(
-        authenticator.BindNewPipeAndPassReceiver());
-    return authenticator;
+  InternalAuthenticatorImpl* GetAuthenticator(
+      const url::Origin& effective_origin_url) {
+    internal_authenticator_impl_ =
+        std::make_unique<InternalAuthenticatorImpl>(main_rfh());
+    internal_authenticator_impl_->SetEffectiveOrigin(effective_origin_url);
+    return internal_authenticator_impl_.get();
   }
 
-  mojo::Remote<blink::mojom::InternalAuthenticator> ConnectToAuthenticator(
-      GURL effective_origin_url,
+  InternalAuthenticatorImpl* ConnectToAuthenticator(
+      const url::Origin& effective_origin_url,
       std::unique_ptr<base::OneShotTimer> timer) {
     internal_authenticator_impl_.reset(new InternalAuthenticatorImpl(
-        main_rfh(), url::Origin::Create(effective_origin_url),
+        main_rfh(),
         std::make_unique<AuthenticatorCommon>(main_rfh(), std::move(timer))));
-    mojo::Remote<blink::mojom::InternalAuthenticator> authenticator;
-    internal_authenticator_impl_->Bind(
-        authenticator.BindNewPipeAndPassReceiver());
-    return authenticator;
+    internal_authenticator_impl_->SetEffectiveOrigin(effective_origin_url);
+    return internal_authenticator_impl_.get();
   }
 
-  mojo::Remote<blink::mojom::InternalAuthenticator>
-  ConstructAuthenticatorWithTimer(
-      GURL effective_origin_url,
+  InternalAuthenticatorImpl* ConstructAuthenticatorWithTimer(
+      const url::Origin& effective_origin_url,
       scoped_refptr<base::TestMockTimeTaskRunner> task_runner) {
     fake_hid_manager_ = std::make_unique<device::FakeFidoHidManager>();
 
@@ -5074,8 +5069,8 @@
     }
 
     NavigateAndCommit(origin);
-    mojo::Remote<blink::mojom::InternalAuthenticator> authenticator =
-        ConnectToAuthenticator(origin);
+    InternalAuthenticatorImpl* authenticator =
+        GetAuthenticator(url::Origin::Create(origin));
     PublicKeyCredentialCreationOptionsPtr options =
         GetTestPublicKeyCredentialCreationOptions();
     options->relying_party.id = test_case.claimed_authority;
@@ -5095,8 +5090,8 @@
     NavigateAndCommit(GURL("https://this.isthewrong.origin"));
     auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
         base::Time::Now(), base::TimeTicks::Now());
-    auto authenticator =
-        ConstructAuthenticatorWithTimer(GURL(test_case.origin), task_runner);
+    auto* authenticator = ConstructAuthenticatorWithTimer(
+        url::Origin::Create(GURL(test_case.origin)), task_runner);
     PublicKeyCredentialCreationOptionsPtr options =
         GetTestPublicKeyCredentialCreationOptions();
     options->relying_party.id = test_case.claimed_authority;
@@ -5126,8 +5121,8 @@
     }
 
     NavigateAndCommit(origin);
-    mojo::Remote<blink::mojom::InternalAuthenticator> authenticator =
-        ConnectToAuthenticator(origin);
+    InternalAuthenticatorImpl* authenticator =
+        GetAuthenticator(url::Origin::Create(origin));
     PublicKeyCredentialRequestOptionsPtr options =
         GetTestPublicKeyCredentialRequestOptions();
     options->relying_party_id = test_case.claimed_authority;
@@ -5149,8 +5144,8 @@
     NavigateAndCommit(GURL("https://this.isthewrong.origin"));
     auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(
         base::Time::Now(), base::TimeTicks::Now());
-    auto authenticator =
-        ConstructAuthenticatorWithTimer(GURL(test_case.origin), task_runner);
+    auto* authenticator = ConstructAuthenticatorWithTimer(
+        url::Origin::Create(GURL(test_case.origin)), task_runner);
     PublicKeyCredentialRequestOptionsPtr options =
         GetTestPublicKeyCredentialRequestOptions();
     options->relying_party_id = test_case.claimed_authority;
diff --git a/content/browser/worker_host/dedicated_worker_host.cc b/content/browser/worker_host/dedicated_worker_host.cc
index 8b064b3..8292b3c 100644
--- a/content/browser/worker_host/dedicated_worker_host.cc
+++ b/content/browser/worker_host/dedicated_worker_host.cc
@@ -65,6 +65,7 @@
       coep_reporter_(std::move(coep_reporter)) {
   DCHECK_CURRENTLY_ON(BrowserThread::UI);
   DCHECK(worker_process_host_);
+  DCHECK(worker_process_host_->IsInitializedAndNotDead());
   DCHECK(coep_reporter_);
 
   scoped_process_host_observer_.Add(worker_process_host_);
@@ -576,7 +577,8 @@
     }
 
     auto* worker_process_host = RenderProcessHost::FromID(worker_process_id_);
-    if (!worker_process_host) {
+    if (!worker_process_host ||
+        !worker_process_host->IsInitializedAndNotDead()) {
       // Abort if the worker's process host is gone. This means that the calling
       // frame or worker is also either destroyed or in the process of being
       // destroyed.
@@ -623,7 +625,8 @@
     // |script_url|, and report as bad message if that fails.
 
     auto* worker_process_host = RenderProcessHost::FromID(worker_process_id_);
-    if (!worker_process_host) {
+    if (!worker_process_host ||
+        !worker_process_host->IsInitializedAndNotDead()) {
       // Abort if the worker's process host is gone. This means that the calling
       // frame or worker is also either destroyed or in the process of being
       // destroyed.
diff --git a/device/fido/client_data.h b/device/fido/client_data.h
index f8651733..579b704 100644
--- a/device/fido/client_data.h
+++ b/device/fido/client_data.h
@@ -17,10 +17,6 @@
 
 namespace device {
 
-// The map key for inserting the googleAndroidClientDataExtension output into a
-// CTAP2 makeCredential or getAssertion response.
-constexpr int kAndroidClientDataExtOutputKey = 0xf0;
-
 // Builds the CollectedClientData[1] dictionary with the given values,
 // serializes it to JSON, and returns the resulting string. For legacy U2F
 // requests coming from the CryptoToken U2F extension, modifies the object key
diff --git a/device/fido/fido_constants.h b/device/fido/fido_constants.h
index c64f740..137fda0 100644
--- a/device/fido/fido_constants.h
+++ b/device/fido/fido_constants.h
@@ -369,6 +369,10 @@
   kUVRequired = 3,
 };
 
+// The map key for inserting the googleAndroidClientDataExtension output into a
+// CTAP2 makeCredential or getAssertion response.
+constexpr int kAndroidClientDataExtOutputKey = 0xf0;
+
 }  // namespace device
 
 #endif  // DEVICE_FIDO_FIDO_CONSTANTS_H_
diff --git a/extensions/browser/extension_function_dispatcher.cc b/extensions/browser/extension_function_dispatcher.cc
index 5b43dcfc..d120282 100644
--- a/extensions/browser/extension_function_dispatcher.cc
+++ b/extensions/browser/extension_function_dispatcher.cc
@@ -51,8 +51,6 @@
 namespace extensions {
 namespace {
 
-constexpr char kCreationFailed[] = "Access to extension API denied.";
-
 // Notifies the ApiActivityMonitor that an extension API function has been
 // called. May be called from any thread.
 void NotifyApiFunctionCalled(const std::string& extension_id,
@@ -336,9 +334,6 @@
     function->set_include_incognito_information(true);
   }
 
-  if (!CheckPermissions(function.get(), params, callback))
-    return;
-
   if (!extension) {
     if (function->source_context_type() == Feature::WEBUI_CONTEXT) {
       base::UmaHistogramSparse("Extensions.Functions.WebUICalls",
@@ -456,19 +451,6 @@
 }
 
 // static
-bool ExtensionFunctionDispatcher::CheckPermissions(
-    ExtensionFunction* function,
-    const ExtensionHostMsg_Request_Params& params,
-    const ExtensionFunction::ResponseCallback& callback) {
-  if (!function->HasPermission()) {
-    LOG(ERROR) << "Permission denied for " << params.name;
-    function->RespondWithError(kCreationFailed);
-    return false;
-  }
-  return true;
-}
-
-// static
 scoped_refptr<ExtensionFunction>
 ExtensionFunctionDispatcher::CreateExtensionFunction(
     const ExtensionHostMsg_Request_Params& params,
@@ -479,6 +461,8 @@
     ExtensionAPI* api,
     void* profile_id,
     const ExtensionFunction::ResponseCallback& callback) {
+  constexpr char kCreationFailed[] = "Access to extension API denied.";
+
   scoped_refptr<ExtensionFunction> function =
       ExtensionFunctionRegistry::GetInstance().NewFunction(params.name);
   if (!function) {
@@ -499,6 +483,12 @@
       extension, requesting_process_id, rfh_url));
   function->set_source_process_id(requesting_process_id);
 
+  if (!function->HasPermission()) {
+    LOG(ERROR) << "Permission denied for " << params.name;
+    function->RespondWithError(kCreationFailed);
+    return nullptr;
+  }
+
   return function;
 }
 }  // namespace extensions
diff --git a/extensions/browser/extension_function_dispatcher.h b/extensions/browser/extension_function_dispatcher.h
index a29c6e1..72c44c7 100644
--- a/extensions/browser/extension_function_dispatcher.h
+++ b/extensions/browser/extension_function_dispatcher.h
@@ -113,15 +113,6 @@
   // |response_callback_wrappers_for_worker_|.
   struct WorkerResponseCallbackMapKey;
 
-  // Helper to check whether an ExtensionFunction has the required permissions.
-  // This should be called after the function is fully initialized.
-  // If the check fails, |callback| is run with an access-denied error and false
-  // is returned. |function| must not be run in that case.
-  static bool CheckPermissions(
-      ExtensionFunction* function,
-      const ExtensionHostMsg_Request_Params& params,
-      const ExtensionFunction::ResponseCallback& callback);
-
   // Helper to create an ExtensionFunction to handle the function given by
   // |params|. Can be called on any thread.
   // Does not set subclass properties, or include_incognito.
diff --git a/gpu/GLES2/extensions/ANGLE/EGL_ANGLE_sync_control_rate.txt b/gpu/GLES2/extensions/ANGLE/EGL_ANGLE_sync_control_rate.txt
new file mode 100644
index 0000000..2056ae8
--- /dev/null
+++ b/gpu/GLES2/extensions/ANGLE/EGL_ANGLE_sync_control_rate.txt
@@ -0,0 +1,110 @@
+Name
+
+   ANGLE_sync_control_rate
+
+Name Strings
+
+   EGL_ANGLE_sync_control_rate
+
+Contact
+
+   Jonah Ryan-Davis, Google (jonahr 'at' google.com)
+
+Status
+
+   Draft.
+
+Version
+
+   Version 1, 2020-03-24
+
+   Based on GLX_OML_sync_control Revision 6.0
+
+Number
+
+   ???
+
+Dependencies
+
+   The extension is written against the EGL 1.2 Specification, although it
+   should work on other versions of these specifications. This extension
+   also requires an operating system which supports CLOCK_MONOTONIC.
+
+Overview
+
+   This extension provides counters which let applications know about the
+   timing of the last vertical retrace. By looking at the system clock, as
+   well as the refresh rate of the monitor, this should enable applications
+   to predict the position of future retraces so as to schedule an optimal
+   workload.
+
+   This extension incorporates the use of three counters that provide
+   the necessary synchronization. The Unadjusted System Time (or UST)
+   is the 64-bit CLOCK_MONOTONIC clock; in particular this lets the
+   application schedule future vertical retraces by querying this clock.
+   The graphics Media Stream Counter (or graphics MSC) is a counter
+   that is unique to the graphics subsystem and increments for each
+   vertical retrace that occurs. The Swap Buffer Counter (SBC) is an
+   attribute of an EGLSurface and is incremented each time a swap
+   buffer action is performed on the associated surface.
+
+   The use of these three counters allows the application to
+   synchronize graphics rendering to vertical retraces and/or swap
+   buffer actions. For example, by querying the synchronization values for
+   a given surface, the application can accurately predict the timing for
+   the next vertical retraces and schedule rendering accordingly.
+
+Issues
+
+   None.
+
+IP Status
+
+   No known issues.
+
+New Procedures and Functions
+
+   Bool eglGetMscRateANGLE(EGLDisplay* dpy,
+                           EGLSurface surface,
+                           int32_t* numerator,
+                           int32_t* denominator)
+
+New Tokens
+
+   None
+
+Additions to the EGL 1.3 Specification
+
+   The graphics MSC value is incremented once for each screen refresh.
+   For a non-interlaced display, this means that the graphics MSC value
+   is incremented for each frame. For an interlaced display, it means
+   that it will be incremented for each field. For a multi-monitor
+   system, the monitor used to determine MSC is the one where the surface
+   is located. If the surface spans multiple monitors, the monitor used
+   to determine MSC is the one with the biggest coverage in pixels.
+
+   eglGetMscRateANGLE returns the rate at which the MSC will be incremented
+   for the display associated with <hdc>. The rate is expressed in Hertz
+   as <numerator> / <denominator>. If the MSC rate in Hertz is an
+   integer, then <denominator> will be 1 and <numerator> will be
+   the MSC rate.
+
+   The function eglGetMscRateANGLE will return TRUE if the function
+   completed successfully, FALSE otherwise.
+
+   Each time eglSwapBuffer succeeds, the SBC will be increased within a
+   finite time period.
+
+Errors
+
+   The function eglGetMscRateANGLE will return FALSE if there is no
+   current EGLContext.
+
+New Implementation Dependent State
+
+   None
+
+Revision History
+
+    Version 1, 2020-03-24 (Jonah Ryan-Davis)
+       - Initial draft, based on GLX_OML_sync_control revision 6.0.
diff --git a/gpu/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_sync_control.txt b/gpu/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_sync_control.txt
index 14f4e56..f699f61 100644
--- a/gpu/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_sync_control.txt
+++ b/gpu/GLES2/extensions/CHROMIUM/EGL_CHROMIUM_sync_control.txt
@@ -16,7 +16,7 @@
 
 Version
 
-   Version 3, 2020-01-23
+   Version 2, 2015-05-05
 
    Based on GLX_OML_sync_control Revision 6.0
 
@@ -70,12 +70,6 @@
                                  int64_t* msc,
                                  int64_t* sbc)
 
-   Bool eglGetMscRateCHROMIUM(EGLDisplay* dpy,
-                              EGLSurface surface,
-                              int32_t* numerator,
-                              int32_t* denominator)
-
-
 New Tokens
 
    None
@@ -112,22 +106,16 @@
    is located. If the surface spans multiple monitors, the monitor used
    to determine MSC is the one with the biggest coverage in pixels.
 
-   eglGetMscRateCHROMIUM returns the rate at which the MSC will be incremented
-   for the display associated with <hdc>. The rate is expressed in Hertz
-   as <numerator> / <denominator>. If the MSC rate in Hertz is an
-   integer, then <denominator> will be 1 and <numerator> will be
-   the MSC rate.
-
-   The functions eglGetSyncValuesCHROMIUM, and eglGetMscRateCHROMIUM will
-   return TRUE if the function completed successfully, FALSE otherwise.
+   The function eglGetSyncValuesCHROMIUM will return TRUE if the function
+   completed successfully, FALSE otherwise.
 
    Each time eglSwapBuffer succeeds, the SBC will be increased within a
    finite time period.
 
 Errors
 
-   The functions eglGetSyncValuesCHROMIUM, and eglGetMscRateCHROMIUM will
-   return FALSE if there is no current EGLContext.
+   The function eglGetSyncValuesCHROMIUM will return FALSE if there is no
+   current EGLContext.
 
 
 New State
@@ -144,10 +132,6 @@
 
 Revision History
 
-    Version 3, 2020-01-23 (Jonah Ryan-Davis)
-       - Add the function eglGetMscRateCHROMIUM based on glXGetMscRateOML from
-         GLX_OML_sync_control revision 6.0.
-
     Version 2, 2015-05-05 (Chad Versace)
        - Rename to EGL_CHROMIUM_sync_control from EGL_CHROMIUM_get_sync_values.
          EGL_CHROMIUM_sync_control is the de facto extension name because all
diff --git a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
index 1c1ad3b5..c31a2d6 100644
--- a/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
+++ b/gpu/command_buffer/service/shared_image_backing_factory_ahardwarebuffer.cc
@@ -287,7 +287,7 @@
 
   ~SharedImageRepresentationSkiaVkAHB() override {
     DCHECK_EQ(mode_, RepresentationAccessMode::kNone);
-    DCHECK(!surface_);
+    surface_.reset();
     DCHECK(vulkan_image_);
     VulkanFenceHelper* fence_helper = context_state_->vk_context_provider()
                                           ->GetDeviceQueue()
@@ -302,29 +302,31 @@
       std::vector<GrBackendSemaphore>* begin_semaphores,
       std::vector<GrBackendSemaphore>* end_semaphores) override {
     DCHECK_EQ(mode_, RepresentationAccessMode::kNone);
-    DCHECK(!surface_);
 
     if (!BeginAccess(false /* readonly */, begin_semaphores, end_semaphores))
       return nullptr;
 
-    SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
-        /*gpu_compositing=*/true, format());
-    auto surface = SkSurface::MakeFromBackendTextureAsRenderTarget(
-        context_state_->gr_context(), promise_texture_->backendTexture(),
-        kTopLeft_GrSurfaceOrigin, final_msaa_count, sk_color_type,
-        color_space().ToSkColorSpace(), &surface_props);
-    DCHECK(surface);
-    surface_ = surface.get();
-    return surface;
+    if (!surface_) {
+      SkColorType sk_color_type = viz::ResourceFormatToClosestSkColorType(
+          /*gpu_compositing=*/true, format());
+      surface_ = SkSurface::MakeFromBackendTextureAsRenderTarget(
+          context_state_->gr_context(), promise_texture_->backendTexture(),
+          kTopLeft_GrSurfaceOrigin, final_msaa_count, sk_color_type,
+          color_space().ToSkColorSpace(), &surface_props);
+    }
+
+    DCHECK(surface_);
+    return surface_;
   }
 
   void EndWriteAccess(sk_sp<SkSurface> surface) override {
     DCHECK_EQ(mode_, RepresentationAccessMode::kWrite);
-    DCHECK_EQ(surface.get(), surface_);
-    DCHECK(surface->unique());
+    DCHECK_EQ(surface.get(), surface_.get());
+
+    surface.reset();
+    DCHECK(surface_->unique());
 
     EndAccess(false /* readonly */);
-    surface_ = nullptr;
   }
 
   sk_sp<SkPromiseImageTexture> BeginReadAccess(
@@ -449,7 +451,7 @@
   std::unique_ptr<VulkanImage> vulkan_image_;
   sk_sp<SkPromiseImageTexture> promise_texture_;
   RepresentationAccessMode mode_ = RepresentationAccessMode::kNone;
-  SkSurface* surface_ = nullptr;
+  sk_sp<SkSurface> surface_;
   scoped_refptr<SharedContextState> context_state_;
   VkSemaphore end_access_semaphore_ = VK_NULL_HANDLE;
 };
diff --git a/gpu/ipc/service/gpu_watchdog_thread_v2.h b/gpu/ipc/service/gpu_watchdog_thread_v2.h
index 6056afb..cba282f6 100644
--- a/gpu/ipc/service/gpu_watchdog_thread_v2.h
+++ b/gpu/ipc/service/gpu_watchdog_thread_v2.h
@@ -124,7 +124,7 @@
   bool ContinueOnNonHostX11ServerTty();
 
   // This counter is only written on the gpu thread, and read on both threads.
-  base::subtle::Atomic32 arm_disarm_counter_ = 0;
+  volatile base::subtle::Atomic32 arm_disarm_counter_ = 0;
   // The counter number read in the last OnWatchdogTimeout() on the watchdog
   // thread.
   int32_t last_arm_disarm_counter_ = 0;
diff --git a/gpu/ipc/service/raster_command_buffer_stub.cc b/gpu/ipc/service/raster_command_buffer_stub.cc
index 72c7849b7..e56d432 100644
--- a/gpu/ipc/service/raster_command_buffer_stub.cc
+++ b/gpu/ipc/service/raster_command_buffer_stub.cc
@@ -103,7 +103,7 @@
   }
 
   if (!shared_context_state->IsGLInitialized()) {
-    if (!shared_context_state->MakeCurrent(nullptr) ||
+    if (!shared_context_state->MakeCurrent(nullptr, true /* needs_gl */) ||
         !shared_context_state->InitializeGL(
             manager->gpu_preferences(),
             base::MakeRefCounted<gles2::FeatureInfo>(
diff --git a/infra/config/buckets/ci.star b/infra/config/buckets/ci.star
index 016a7236c..617e51d 100644
--- a/infra/config/buckets/ci.star
+++ b/infra/config/buckets/ci.star
@@ -997,6 +997,7 @@
     use_clang_coverage = True,
     properties = {
         'xcode_build_version': '11c29',
+        'coverage_test_types': ['overall', 'unit'],
     },
 )
 
diff --git a/infra/config/buckets/try.star b/infra/config/buckets/try.star
index 9961ed0..82b04111 100644
--- a/infra/config/buckets/try.star
+++ b/infra/config/buckets/try.star
@@ -551,6 +551,9 @@
     executable = 'recipe:chromium_trybot',
     goma_backend = None,  # TODO(crbug.com/950413): Use goma.backend.RBE_PROD
     use_clang_coverage = True,
+    properties = {
+        'coverage_test_types': ['unit'],
+    },
 )
 
 try_.chromium_mac_ios_builder(
diff --git a/infra/config/generated/cr-buildbucket.cfg b/infra/config/generated/cr-buildbucket.cfg
index 5549f9a..6968b48 100644
--- a/infra/config/generated/cr-buildbucket.cfg
+++ b/infra/config/generated/cr-buildbucket.cfg
@@ -6904,6 +6904,7 @@
         cipd_version: "refs/heads/master"
         properties_j: "$build/code_coverage:{\"use_clang_coverage\":true}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "coverage_test_types:[\"overall\",\"unit\"]"
         properties_j: "mastername:\"chromium.fyi\""
         properties_j: "xcode_build_version:\"11c29\""
       >
@@ -15502,8 +15503,8 @@
         cipd_version: "refs/heads/master"
         properties_j: "$build/code_coverage:{\"use_clang_coverage\":true}"
         properties_j: "$kitchen:{\"devshell\":true,\"git_auth\":true}"
+        properties_j: "coverage_test_types:[\"unit\"]"
         properties_j: "mastername:\"tryserver.chromium.mac\""
-        properties_j: "xcode_build_version:\"11c29\""
       >
       execution_timeout_secs: 14400
       expiration_secs: 7200
diff --git a/infra/config/lib/bucket.star b/infra/config/lib/bucket.star
deleted file mode 100644
index c985831..0000000
--- a/infra/config/lib/bucket.star
+++ /dev/null
@@ -1,9 +0,0 @@
-def var(*, default):
-  var = lucicfg.var(default = default)
-  def builder(builder):
-    return '{}/{}'.format(var.get(), builder)
-  return struct(
-      builder = builder,
-      get = var.get,
-      set = var.set,
-  )
diff --git a/infra/config/lib/builders.star b/infra/config/lib/builders.star
index 575b241..e18d7fb 100644
--- a/infra/config/lib/builders.star
+++ b/infra/config/lib/builders.star
@@ -447,8 +447,16 @@
   )
 
 
+def builder_name(builder, bucket=args.DEFAULT):
+  bucket = defaults.get_value('bucket', bucket)
+  if bucket == args.COMPUTE:
+    fail('Either a default for bucket must be set or bucket must be passed in')
+  return '{}/{}'.format(bucket, builder)
+
+
 builders = struct(
     builder = builder,
+    builder_name = builder_name,
     cpu = cpu,
     defaults = defaults,
     goma = goma,
diff --git a/infra/config/versioned/README.md b/infra/config/versioned/README.md
index 5b9ee170..b582c006 100644
--- a/infra/config/versioned/README.md
+++ b/infra/config/versioned/README.md
@@ -2,14 +2,6 @@
 
 Contents:
 
-* **branches**
-  * contains subdirectories that contain shims that set branch-specific
-  variables before executing the configuration in the appropriate **milestones**
-  subdirectory
-  * doesn't contain much actual configuration, so changes should be extremely
-  rare
-* **milestones.star**
-  * module that maps the branches `beta` and `stable` to the milestones
 * **milestones**
   * contains subdirectories that contain the versioned configuration for the
   active milestones
@@ -17,7 +9,4 @@
 * **trunk**
   * contains the versioned configuration for the current canary/dev builds
   * open to changes
-* **vars**
-  * contains modules with branch-specific variables; the variables are set by
-  the shims in **branches** when executing the configuration in **milestones**
 
diff --git a/infra/config/versioned/milestones/m80/buckets/ci.star b/infra/config/versioned/milestones/m80/buckets/ci.star
index e6869935..b0da041c 100644
--- a/infra/config/versioned/milestones/m80/buckets/ci.star
+++ b/infra/config/versioned/milestones/m80/buckets/ci.star
@@ -1,17 +1,13 @@
-load('//lib/builders.star', 'cpu', 'defaults', 'goma', 'os')
+load('//lib/builders.star', 'builder_name', 'cpu', 'defaults', 'goma', 'os')
 load('//lib/ci.star', 'ci')
-load('//versioned/vars/ci.star', 'vars')
 # Load this using relative path so that the load statement doesn't
 # need to be changed when making a new milestone
-load('../vars.star', milestone_vars='vars')
-
-vars.bucket.set('ci-m80')
-vars.poller.set('m80-gitiles-trigger')
+load('../vars.star', 'vars')
 
 defaults.pool.set('luci.chromium.ci')
 
 luci.bucket(
-    name = vars.bucket.get(),
+    name = vars.ci_bucket,
     acls = [
         acl.entry(
             roles = acl.BUILDBUCKET_READER,
@@ -29,16 +25,16 @@
 )
 
 luci.gitiles_poller(
-    name = vars.poller.get(),
-    bucket = vars.bucket.get(),
+    name = vars.ci_poller,
+    bucket = vars.ci_bucket,
     repo = 'https://chromium.googlesource.com/chromium/src',
-    refs = [milestone_vars.ref],
+    refs = [vars.ref],
 )
 
 
-ci.defaults.bucket.set(vars.bucket.get())
+ci.defaults.bucket.set(vars.ci_bucket)
 ci.defaults.bucketed_triggers.set(True)
-ci.defaults.triggered_by.set([vars.poller.get()])
+ci.defaults.triggered_by.set([vars.ci_poller])
 
 
 # Builders are sorted first lexicographically by the function used to define
@@ -68,7 +64,7 @@
 ci.fyi_builder(
     name = 'mac-osxbeta-rel',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 
@@ -76,7 +72,7 @@
     name = 'Win10 Tests x64 1803',
     os = os.WINDOWS_10,
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
 
 
@@ -103,22 +99,22 @@
 
 ci.gpu_thin_tester(
     name = 'Linux Release (NVIDIA)',
-    triggered_by = [vars.bucket.builder('GPU Linux Builder')],
+    triggered_by = [builder_name('GPU Linux Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Mac Release (Intel)',
-    triggered_by = [vars.bucket.builder('GPU Mac Builder')],
+    triggered_by = [builder_name('GPU Mac Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Mac Retina Release (AMD)',
-    triggered_by = [vars.bucket.builder('GPU Mac Builder')],
+    triggered_by = [builder_name('GPU Mac Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Win10 x64 Release (NVIDIA)',
-    triggered_by = [vars.bucket.builder('GPU Win x64 Builder')],
+    triggered_by = [builder_name('GPU Win x64 Builder')],
 )
 
 
@@ -129,7 +125,7 @@
 ci.linux_builder(
     name = 'Linux Tests',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('Linux Builder')],
+    triggered_by = [builder_name('Linux Builder')],
 )
 
 ci.linux_builder(
@@ -139,13 +135,13 @@
 ci.linux_builder(
     name = 'Linux Ozone Tester (Wayland)',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('linux-ozone-rel')],
+    triggered_by = [builder_name('linux-ozone-rel')],
 )
 
 ci.linux_builder(
     name = 'Linux Ozone Tester (X11)',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('linux-ozone-rel')],
+    triggered_by = [builder_name('linux-ozone-rel')],
 )
 
 
@@ -157,37 +153,37 @@
 # The build runs on 10.13, but triggers tests on 10.10 bots.
 ci.mac_builder(
     name = 'Mac10.10 Tests',
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 # The build runs on 10.13, but triggers tests on 10.11 bots.
 ci.mac_builder(
     name = 'Mac10.11 Tests',
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.12 Tests',
     os = os.MAC_10_12,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.13 Tests',
     os = os.MAC_10_13,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.14 Tests',
     os = os.MAC_10_14,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'WebKit Mac10.13 (retina)',
     os = os.MAC_10_13,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 
@@ -200,7 +196,7 @@
 ci.win_builder(
     name = 'Win 7 Tests x64 (1)',
     os = os.WINDOWS_7,
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
 
 ci.win_builder(
@@ -211,5 +207,5 @@
 
 ci.win_builder(
     name = 'Win10 Tests x64',
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
diff --git a/infra/config/versioned/milestones/m80/buckets/try.star b/infra/config/versioned/milestones/m80/buckets/try.star
index 938ee258..8d68b421 100644
--- a/infra/config/versioned/milestones/m80/buckets/try.star
+++ b/infra/config/versioned/milestones/m80/buckets/try.star
@@ -1,17 +1,13 @@
 load('//lib/builders.star', 'cpu', 'defaults', 'goma', 'os')
 load('//lib/try.star', 'try_')
-load('//versioned/vars/try.star', 'vars')
 # Load this using relative path so that the load statement doesn't
 # need to be changed when making a new milestone
-load('../vars.star', milestone_vars='vars')
-
-vars.bucket.set('try-m80')
-vars.cq_group.set('cq-m80')
+load('../vars.star', 'vars')
 
 defaults.pool.set('luci.chromium.try')
 
 luci.bucket(
-    name = vars.bucket.get(),
+    name = vars.try_bucket,
     acls = [
         acl.entry(
             roles = acl.BUILDBUCKET_READER,
@@ -38,12 +34,12 @@
 )
 
 luci.cq_group(
-    name = vars.cq_group.get(),
+    name = vars.cq_group,
     cancel_stale_tryjobs = True,
     retry_config = cq.RETRY_ALL_FAILURES,
     watch = cq.refset(
         repo = 'https://chromium.googlesource.com/chromium/src',
-        refs = [milestone_vars.cq_ref_regexp],
+        refs = [vars.cq_ref_regexp],
     ),
     acls = [
         acl.entry(
@@ -57,8 +53,8 @@
     ],
 )
 
-try_.defaults.bucket.set(vars.bucket.get())
-try_.defaults.cq_group.set(vars.cq_group.get())
+try_.defaults.bucket.set(vars.try_bucket)
+try_.defaults.cq_group.set(vars.cq_group)
 
 
 # Builders are sorted first lexicographically by the function used to define
diff --git a/infra/config/versioned/milestones/m80/vars.star b/infra/config/versioned/milestones/m80/vars.star
index ef5726d..6e388a27 100644
--- a/infra/config/versioned/milestones/m80/vars.star
+++ b/infra/config/versioned/milestones/m80/vars.star
@@ -1,4 +1,8 @@
 vars = struct(
     ref = 'refs/branch-heads/3987',
+    ci_bucket = 'ci-m80',
+    ci_poller = 'm80-gitiles-trigger',
+    try_bucket = 'try-m80',
+    cq_group = 'cq-m80',
     cq_ref_regexp = 'refs/branch-heads/3987'
 )
diff --git a/infra/config/versioned/milestones/m81/buckets/ci.star b/infra/config/versioned/milestones/m81/buckets/ci.star
index 34cb68a4..72c40e41 100644
--- a/infra/config/versioned/milestones/m81/buckets/ci.star
+++ b/infra/config/versioned/milestones/m81/buckets/ci.star
@@ -1,17 +1,13 @@
-load('//lib/builders.star', 'cpu', 'defaults', 'goma', 'os')
+load('//lib/builders.star', 'builder_name', 'cpu', 'defaults', 'goma', 'os')
 load('//lib/ci.star', 'ci')
-load('//versioned/vars/ci.star', 'vars')
 # Load this using relative path so that the load statement doesn't
 # need to be changed when making a new milestone
-load('../vars.star', milestone_vars='vars')
-
-vars.bucket.set('ci-m81')
-vars.poller.set('m81-gitiles-trigger')
+load('../vars.star', 'vars')
 
 defaults.pool.set('luci.chromium.ci')
 
 luci.bucket(
-    name = vars.bucket.get(),
+    name = vars.ci_bucket,
     acls = [
         acl.entry(
             roles = acl.BUILDBUCKET_READER,
@@ -29,16 +25,16 @@
 )
 
 luci.gitiles_poller(
-    name = vars.poller.get(),
-    bucket = vars.bucket.get(),
+    name = vars.ci_poller,
+    bucket = vars.ci_bucket,
     repo = 'https://chromium.googlesource.com/chromium/src',
-    refs = [milestone_vars.ref],
+    refs = [vars.ref],
 )
 
 
-ci.defaults.bucket.set(vars.bucket.get())
+ci.defaults.bucket.set(vars.ci_bucket)
 ci.defaults.bucketed_triggers.set(True)
-ci.defaults.triggered_by.set([vars.poller.get()])
+ci.defaults.triggered_by.set([vars.ci_poller])
 
 
 # Builders are sorted first lexicographically by the function used to define
@@ -62,13 +58,13 @@
 ci.android_builder(
     name = 'android-cronet-kitkat-arm-rel',
     notifies = ['cronet'],
-    triggered_by = [vars.bucket.builder('android-cronet-arm-rel')],
+    triggered_by = [builder_name('android-cronet-arm-rel')],
 )
 
 ci.android_builder(
     name = 'android-cronet-lollipop-arm-rel',
     notifies = ['cronet'],
-    triggered_by = [vars.bucket.builder('android-cronet-arm-rel')],
+    triggered_by = [builder_name('android-cronet-arm-rel')],
 )
 
 ci.android_builder(
@@ -102,7 +98,7 @@
 ci.fyi_builder(
     name = 'mac-osxbeta-rel',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 
@@ -110,7 +106,7 @@
     name = 'Win10 Tests x64 1803',
     goma_backend = None,
     os = os.WINDOWS_10,
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
 
 
@@ -137,22 +133,22 @@
 
 ci.gpu_thin_tester(
     name = 'Linux Release (NVIDIA)',
-    triggered_by = [vars.bucket.builder('GPU Linux Builder')],
+    triggered_by = [builder_name('GPU Linux Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Mac Release (Intel)',
-    triggered_by = [vars.bucket.builder('GPU Mac Builder')],
+    triggered_by = [builder_name('GPU Mac Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Mac Retina Release (AMD)',
-    triggered_by = [vars.bucket.builder('GPU Mac Builder')],
+    triggered_by = [builder_name('GPU Mac Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Win10 x64 Release (NVIDIA)',
-    triggered_by = [vars.bucket.builder('GPU Win x64 Builder')],
+    triggered_by = [builder_name('GPU Win x64 Builder')],
 )
 
 
@@ -178,7 +174,7 @@
 ci.linux_builder(
     name = 'Linux Tests',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('Linux Builder')],
+    triggered_by = [builder_name('Linux Builder')],
 )
 
 ci.linux_builder(
@@ -188,13 +184,13 @@
 ci.linux_builder(
     name = 'Linux Ozone Tester (Wayland)',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('linux-ozone-rel')],
+    triggered_by = [builder_name('linux-ozone-rel')],
 )
 
 ci.linux_builder(
     name = 'Linux Ozone Tester (X11)',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('linux-ozone-rel')],
+    triggered_by = [builder_name('linux-ozone-rel')],
 )
 
 ci.mac_builder(
@@ -210,43 +206,43 @@
 # The build runs on 10.13, but triggers tests on 10.10 bots.
 ci.mac_builder(
     name = 'Mac10.10 Tests',
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 # The build runs on 10.13, but triggers tests on 10.11 bots.
 ci.mac_builder(
     name = 'Mac10.11 Tests',
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.12 Tests',
     os = os.MAC_10_12,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.13 Tests',
     os = os.MAC_10_13,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.14 Tests',
     os = os.MAC_10_14,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.13 Tests (dbg)',
     os = os.MAC_ANY,
-    triggered_by = [vars.bucket.builder('Mac Builder (dbg)')],
+    triggered_by = [builder_name('Mac Builder (dbg)')],
 )
 
 ci.mac_builder(
     name = 'WebKit Mac10.13 (retina)',
     os = os.MAC_10_13,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 
@@ -263,25 +259,25 @@
 
 ci.memory_builder(
     name = 'Linux ASan LSan Tests (1)',
-    triggered_by = [vars.bucket.builder('Linux ASan LSan Builder')],
+    triggered_by = [builder_name('Linux ASan LSan Builder')],
 )
 
 ci.memory_builder(
     name = 'Linux ASan Tests (sandboxed)',
-    triggered_by = [vars.bucket.builder('Linux ASan LSan Builder')],
+    triggered_by = [builder_name('Linux ASan LSan Builder')],
 )
 
 
 ci.win_builder(
     name = 'Win7 Tests (dbg)(1)',
     os = os.WINDOWS_7,
-    triggered_by = [vars.bucket.builder('Win Builder (dbg)')],
+    triggered_by = [builder_name('Win Builder (dbg)')],
 )
 
 ci.win_builder(
     name = 'Win 7 Tests x64 (1)',
     os = os.WINDOWS_7,
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
 
 ci.win_builder(
@@ -298,5 +294,5 @@
 
 ci.win_builder(
     name = 'Win10 Tests x64',
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
diff --git a/infra/config/versioned/milestones/m81/buckets/try.star b/infra/config/versioned/milestones/m81/buckets/try.star
index 4c425684..6ad62b3 100644
--- a/infra/config/versioned/milestones/m81/buckets/try.star
+++ b/infra/config/versioned/milestones/m81/buckets/try.star
@@ -1,17 +1,13 @@
 load('//lib/builders.star', 'cpu', 'defaults', 'goma', 'os')
 load('//lib/try.star', 'try_')
-load('//versioned/vars/try.star', 'vars')
 # Load this using relative path so that the load statement doesn't
 # need to be changed when making a new milestone
-load('../vars.star', milestone_vars='vars')
-
-vars.bucket.set('try-m81')
-vars.cq_group.set('cq-m81')
+load('../vars.star', 'vars')
 
 defaults.pool.set('luci.chromium.try')
 
 luci.bucket(
-    name = vars.bucket.get(),
+    name = vars.try_bucket,
     acls = [
         acl.entry(
             roles = acl.BUILDBUCKET_READER,
@@ -38,12 +34,12 @@
 )
 
 luci.cq_group(
-    name = vars.cq_group.get(),
+    name = vars.cq_group,
     cancel_stale_tryjobs = True,
     retry_config = cq.RETRY_ALL_FAILURES,
     watch = cq.refset(
         repo = 'https://chromium.googlesource.com/chromium/src',
-        refs = [milestone_vars.cq_ref_regexp],
+        refs = [vars.cq_ref_regexp],
     ),
     acls = [
         acl.entry(
@@ -57,8 +53,8 @@
     ],
 )
 
-try_.defaults.bucket.set(vars.bucket.get())
-try_.defaults.cq_group.set(vars.cq_group.get())
+try_.defaults.bucket.set(vars.try_bucket)
+try_.defaults.cq_group.set(vars.cq_group)
 
 
 # Builders are sorted first lexicographically by the function used to define
diff --git a/infra/config/versioned/milestones/m81/vars.star b/infra/config/versioned/milestones/m81/vars.star
index 752d2ed3..592db51 100644
--- a/infra/config/versioned/milestones/m81/vars.star
+++ b/infra/config/versioned/milestones/m81/vars.star
@@ -1,4 +1,8 @@
 vars = struct(
     ref = 'refs/branch-heads/4044',
+    ci_bucket = 'ci-m81',
+    ci_poller = 'm81-gitiles-trigger',
+    try_bucket = 'try-m81',
+    cq_group = 'cq-m81',
     cq_ref_regexp = 'refs/branch-heads/4044',
 )
diff --git a/infra/config/versioned/trunk/buckets/ci.star b/infra/config/versioned/trunk/buckets/ci.star
index d22dcdf..d671b2da 100644
--- a/infra/config/versioned/trunk/buckets/ci.star
+++ b/infra/config/versioned/trunk/buckets/ci.star
@@ -1,12 +1,11 @@
-load('//lib/builders.star', 'cpu', 'goma', 'os')
+load('//lib/builders.star', 'builder_name', 'cpu', 'goma', 'os')
 load('//lib/ci.star', 'ci')
-load('//versioned/vars/ci.star', 'vars')
 # Load this using relative path so that the load statement doesn't
 # need to be changed when making a new milestone
-load('../vars.star', milestone_vars='vars')
+load('../vars.star', 'vars')
 
 luci.bucket(
-    name = vars.bucket.get(),
+    name = vars.ci_bucket,
     acls = [
         acl.entry(
             roles = acl.BUILDBUCKET_READER,
@@ -24,16 +23,16 @@
 )
 
 luci.gitiles_poller(
-    name = vars.poller.get(),
-    bucket = vars.bucket.get(),
+    name = vars.ci_poller,
+    bucket = vars.ci_bucket,
     repo = 'https://chromium.googlesource.com/chromium/src',
-    refs = [milestone_vars.ref],
+    refs = [vars.ref],
 )
 
 
-ci.defaults.bucket.set(vars.bucket.get())
+ci.defaults.bucket.set(vars.ci_bucket)
 ci.defaults.bucketed_triggers.set(True)
-ci.defaults.triggered_by.set([vars.poller.get()])
+ci.defaults.triggered_by.set([vars.ci_poller])
 
 
 # Builders are sorted first lexicographically by the function used to define
@@ -42,22 +41,22 @@
 
 ci.android_builder(
     name = 'Android WebView M (dbg)',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
     name = 'Android WebView N (dbg)',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
     name = 'Android WebView O (dbg)',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
     name = 'Android WebView P (dbg)',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
@@ -86,17 +85,17 @@
 
 ci.android_builder(
     name = 'Marshmallow 64 bit Tester',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
     name = 'Nougat Phone Tester',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
     name = 'Oreo Phone Tester',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
@@ -112,13 +111,13 @@
 ci.android_builder(
     name = 'android-cronet-kitkat-arm-rel',
     notifies = ['cronet'],
-    triggered_by = [vars.bucket.builder('android-cronet-arm-rel')],
+    triggered_by = [builder_name('android-cronet-arm-rel')],
 )
 
 ci.android_builder(
     name = 'android-cronet-lollipop-arm-rel',
     notifies = ['cronet'],
-    triggered_by = [vars.bucket.builder('android-cronet-arm-rel')],
+    triggered_by = [builder_name('android-cronet-arm-rel')],
 )
 
 ci.android_builder(
@@ -131,7 +130,7 @@
 
 ci.android_builder(
     name = 'android-pie-arm64-dbg',
-    triggered_by = [vars.bucket.builder('Android arm64 Builder (dbg)')],
+    triggered_by = [builder_name('Android arm64 Builder (dbg)')],
 )
 
 ci.android_builder(
@@ -176,14 +175,14 @@
     name = 'Dawn Linux x64 DEPS Release (Intel HD 630)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Linux x64 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Linux x64 DEPS Builder')],
 )
 
 ci.dawn_builder(
     name = 'Dawn Linux x64 DEPS Release (NVIDIA)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Linux x64 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Linux x64 DEPS Builder')],
 )
 
 ci.dawn_builder(
@@ -199,14 +198,14 @@
     name = 'Dawn Mac x64 DEPS Release (AMD)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Mac x64 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Mac x64 DEPS Builder')],
 )
 
 ci.dawn_builder(
     name = 'Dawn Mac x64 DEPS Release (Intel)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Mac x64 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Mac x64 DEPS Builder')],
 )
 
 ci.dawn_builder(
@@ -218,14 +217,14 @@
     name = 'Dawn Win10 x64 DEPS Release (Intel HD 630)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Win10 x64 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Win10 x64 DEPS Builder')],
 )
 
 ci.dawn_builder(
     name = 'Dawn Win10 x64 DEPS Release (NVIDIA)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Win10 x64 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Win10 x64 DEPS Builder')],
 )
 
 ci.dawn_builder(
@@ -237,14 +236,14 @@
     name = 'Dawn Win10 x86 DEPS Release (Intel HD 630)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Win10 x86 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Win10 x86 DEPS Builder')],
 )
 
 ci.dawn_builder(
     name = 'Dawn Win10 x86 DEPS Release (NVIDIA)',
     cores = 2,
     os = os.LINUX_DEFAULT,
-    triggered_by = [vars.bucket.builder('Dawn Win10 x86 DEPS Builder')],
+    triggered_by = [builder_name('Dawn Win10 x86 DEPS Builder')],
 )
 
 
@@ -257,7 +256,7 @@
 ci.fyi_builder(
     name = 'mac-osxbeta-rel',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 
@@ -265,7 +264,7 @@
     name = 'Win10 Tests x64 1803',
     goma_backend = None,
     os = os.WINDOWS_10,
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
 
 
@@ -292,22 +291,22 @@
 
 ci.gpu_thin_tester(
     name = 'Linux Release (NVIDIA)',
-    triggered_by = [vars.bucket.builder('GPU Linux Builder')],
+    triggered_by = [builder_name('GPU Linux Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Mac Release (Intel)',
-    triggered_by = [vars.bucket.builder('GPU Mac Builder')],
+    triggered_by = [builder_name('GPU Mac Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Mac Retina Release (AMD)',
-    triggered_by = [vars.bucket.builder('GPU Mac Builder')],
+    triggered_by = [builder_name('GPU Mac Builder')],
 )
 
 ci.gpu_thin_tester(
     name = 'Win10 x64 Release (NVIDIA)',
-    triggered_by = [vars.bucket.builder('GPU Win x64 Builder')],
+    triggered_by = [builder_name('GPU Win x64 Builder')],
 )
 
 
@@ -337,12 +336,12 @@
 ci.linux_builder(
     name = 'Linux Tests',
     goma_backend = None,
-    triggered_by = [vars.bucket.builder('Linux Builder')],
+    triggered_by = [builder_name('Linux Builder')],
 )
 
 ci.linux_builder(
     name = 'Linux Tests (dbg)(1)',
-    triggered_by = [vars.bucket.builder('Linux Builder (dbg)')],
+    triggered_by = [builder_name('Linux Builder (dbg)')],
 )
 
 ci.linux_builder(
@@ -366,12 +365,12 @@
 
 ci.linux_builder(
     name = 'Linux Ozone Tester (Wayland)',
-    triggered_by = [vars.bucket.builder('linux-ozone-rel')],
+    triggered_by = [builder_name('linux-ozone-rel')],
 )
 
 ci.linux_builder(
     name = 'Linux Ozone Tester (X11)',
-    triggered_by = [vars.bucket.builder('linux-ozone-rel')],
+    triggered_by = [builder_name('linux-ozone-rel')],
 )
 
 
@@ -388,43 +387,43 @@
 # The build runs on 10.13, but triggers tests on 10.10 bots.
 ci.mac_builder(
     name = 'Mac10.10 Tests',
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 # The build runs on 10.13, but triggers tests on 10.11 bots.
 ci.mac_builder(
     name = 'Mac10.11 Tests',
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.12 Tests',
     os = os.MAC_10_12,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.13 Tests',
     os = os.MAC_10_13,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.14 Tests',
     os = os.MAC_10_14,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 ci.mac_builder(
     name = 'Mac10.13 Tests (dbg)',
     os = os.MAC_ANY,
-    triggered_by = [vars.bucket.builder('Mac Builder (dbg)')],
+    triggered_by = [builder_name('Mac Builder (dbg)')],
 )
 
 ci.mac_builder(
     name = 'WebKit Mac10.13 (retina)',
     os = os.MAC_10_13,
-    triggered_by = [vars.bucket.builder('Mac Builder')],
+    triggered_by = [builder_name('Mac Builder')],
 )
 
 
@@ -440,25 +439,25 @@
 
 ci.memory_builder(
     name = 'Linux ASan LSan Tests (1)',
-    triggered_by = [vars.bucket.builder('Linux ASan LSan Builder')],
+    triggered_by = [builder_name('Linux ASan LSan Builder')],
 )
 
 ci.memory_builder(
     name = 'Linux ASan Tests (sandboxed)',
-    triggered_by = [vars.bucket.builder('Linux ASan LSan Builder')],
+    triggered_by = [builder_name('Linux ASan LSan Builder')],
 )
 
 
 ci.win_builder(
     name = 'Win7 Tests (dbg)(1)',
     os = os.WINDOWS_7,
-    triggered_by = [vars.bucket.builder('Win Builder (dbg)')],
+    triggered_by = [builder_name('Win Builder (dbg)')],
 )
 
 ci.win_builder(
     name = 'Win 7 Tests x64 (1)',
     os = os.WINDOWS_7,
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
 
 ci.win_builder(
@@ -475,5 +474,5 @@
 
 ci.win_builder(
     name = 'Win10 Tests x64',
-    triggered_by = [vars.bucket.builder('Win x64 Builder')],
+    triggered_by = [builder_name('Win x64 Builder')],
 )
diff --git a/infra/config/versioned/trunk/buckets/try.star b/infra/config/versioned/trunk/buckets/try.star
index d441c32..83403d5c 100644
--- a/infra/config/versioned/trunk/buckets/try.star
+++ b/infra/config/versioned/trunk/buckets/try.star
@@ -1,12 +1,11 @@
 load('//lib/builders.star', 'cpu', 'goma', 'os')
 load('//lib/try.star', 'try_')
-load('//versioned/vars/try.star', 'vars')
 # Load this using relative path so that the load statement doesn't
 # need to be changed when making a new milestone
-load('../vars.star', milestone_vars='vars')
+load('../vars.star', 'vars')
 
 luci.bucket(
-    name = vars.bucket.get(),
+    name = vars.try_bucket,
     acls = [
         acl.entry(
             roles = acl.BUILDBUCKET_READER,
@@ -33,13 +32,13 @@
 )
 
 luci.cq_group(
-    name = vars.cq_group.get(),
+    name = vars.cq_group,
     cancel_stale_tryjobs = True,
     retry_config = cq.RETRY_ALL_FAILURES,
-    tree_status_host = getattr(milestone_vars, 'tree_status_host', None),
+    tree_status_host = getattr(vars, 'tree_status_host', None),
     watch = cq.refset(
         repo = 'https://chromium.googlesource.com/chromium/src',
-        refs = [milestone_vars.cq_ref_regexp],
+        refs = [vars.cq_ref_regexp],
     ),
     acls = [
         acl.entry(
@@ -53,8 +52,8 @@
     ],
 )
 
-try_.defaults.bucket.set(vars.bucket.get())
-try_.defaults.cq_group.set(vars.cq_group.get())
+try_.defaults.bucket.set(vars.try_bucket)
+try_.defaults.cq_group.set(vars.cq_group)
 
 
 # Builders are sorted first lexicographically by the function used to define
diff --git a/infra/config/versioned/trunk/vars.star b/infra/config/versioned/trunk/vars.star
index 1c1c35b..9b6c8ff0 100644
--- a/infra/config/versioned/trunk/vars.star
+++ b/infra/config/versioned/trunk/vars.star
@@ -1,5 +1,9 @@
 vars = struct(
     ref = 'refs/heads/master',
+    ci_bucket = 'ci',
+    ci_poller = 'master-gitiles-trigger',
+    try_bucket = 'try',
+    cq_group = 'cq',
     cq_ref_regexp = 'refs/heads/.+',
     # Delete this line for branches
     tree_status_host = 'chromium-status.appspot.com/',
diff --git a/infra/config/versioned/vars/ci.star b/infra/config/versioned/vars/ci.star
deleted file mode 100644
index 02ff969..0000000
--- a/infra/config/versioned/vars/ci.star
+++ /dev/null
@@ -1,7 +0,0 @@
-load('//lib/bucket.star', bucket_var='var')
-
-vars = struct(
-    bucket = bucket_var(default = 'ci'),
-    poller = lucicfg.var(default = 'master-gitiles-trigger'),
-    poller_refs_regexp = lucicfg.var(default = 'refs/head/master'),
-)
diff --git a/infra/config/versioned/vars/try.star b/infra/config/versioned/vars/try.star
deleted file mode 100644
index 8e952c5..0000000
--- a/infra/config/versioned/vars/try.star
+++ /dev/null
@@ -1,6 +0,0 @@
-load('//lib/bucket.star', bucket_var='var')
-
-vars = struct(
-    bucket = bucket_var(default = 'try'),
-    cq_group = lucicfg.var(default = 'cq'),
-)
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd
index 3717fdb..a9c41912 100644
--- a/ios/chrome/app/strings/ios_strings.grd
+++ b/ios/chrome/app/strings/ios_strings.grd
@@ -1591,6 +1591,9 @@
       <message name="IDS_IOS_SHARE_MENU_PRINT_ACTION" desc="The text label of the Print action in the extension menu that starts the printing process. [Length: 13em] [iOS only]">
         Print
       </message>
+      <message name="IDS_IOS_SHARE_MENU_GENERATE_QR_CODE_ACTION" desc="The text label of the Share as QR Code action in the extension menu that generates and shows a QR code for the current page. [iOS only]">
+        Share as QR code
+      </message>
       <message name="IDS_IOS_SHARE_MENU_READING_LIST_ACTION" desc="The text label of the Add To Reading List action in the extension menu that adds the current page to the reading list. [Length: 25em] [iOS only]">
         Read Later
       </message>
diff --git a/ios/chrome/browser/chrome_url_constants.cc b/ios/chrome/browser/chrome_url_constants.cc
index 48913bf..9540c80 100644
--- a/ios/chrome/browser/chrome_url_constants.cc
+++ b/ios/chrome/browser/chrome_url_constants.cc
@@ -15,6 +15,7 @@
 const char kChromeUIFlagsURL[] = "chrome://flags/";
 const char kChromeUIHistoryURL[] = "chrome://history/";
 const char kChromeUIInspectURL[] = "chrome://inspect/";
+const char kChromeUIIntersitialsURL[] = "chrome://interstitials";
 const char kChromeUINewTabURL[] = "chrome://newtab/";
 const char kChromeUINTPTilesInternalsURL[] = "chrome://ntp-tiles-internals/";
 const char kChromeUIOfflineURL[] = "chrome://offline/";
@@ -36,6 +37,7 @@
 const char kChromeUIHistogramHost[] = "histograms";
 const char kChromeUIHistoryHost[] = "history";
 const char kChromeUIInspectHost[] = "inspect";
+const char kChromeUIIntersitialsHost[] = "interstitials";
 const char kChromeUINetExportHost[] = "net-export";
 const char kChromeUINewTabHost[] = "newtab";
 const char kChromeUINTPTilesInternalsHost[] = "ntp-tiles-internals";
diff --git a/ios/chrome/browser/chrome_url_constants.h b/ios/chrome/browser/chrome_url_constants.h
index 715781a..70a502b 100644
--- a/ios/chrome/browser/chrome_url_constants.h
+++ b/ios/chrome/browser/chrome_url_constants.h
@@ -25,6 +25,7 @@
 extern const char kChromeUIFlagsURL[];
 extern const char kChromeUIHistoryURL[];
 extern const char kChromeUIInspectURL[];
+extern const char kChromeUIIntersitialsURL[];
 extern const char kChromeUINewTabURL[];
 extern const char kChromeUINTPTilesInternalsURL[];
 extern const char kChromeUIOfflineURL[];
@@ -48,6 +49,7 @@
 extern const char kChromeUIHistogramHost[];
 extern const char kChromeUIHistoryHost[];
 extern const char kChromeUIInspectHost[];
+extern const char kChromeUIIntersitialsHost[];
 extern const char kChromeUINetExportHost[];
 extern const char kChromeUINewTabHost[];
 extern const char kChromeUINTPTilesInternalsHost[];
diff --git a/ios/chrome/browser/metrics/ukm_egtest.mm b/ios/chrome/browser/metrics/ukm_egtest.mm
index 8898e1e..96c2d6b 100644
--- a/ios/chrome/browser/metrics/ukm_egtest.mm
+++ b/ios/chrome/browser/metrics/ukm_egtest.mm
@@ -200,12 +200,13 @@
 // //chrome/browser/metrics/ukm_browsertest.cc
 
 // Make sure that UKM is disabled while an incognito tab is open.
-- (void)testRegularPlusIncognito {
 #if defined(CHROME_EARL_GREY_1)
-  // TODO(crbug.com/1033726): EG1 Test fails on iOS 12.
-  EARL_GREY_TEST_DISABLED(@"EG1 Fails");
+// TODO(crbug.com/1033726): EG1 Test fails on iOS 12.
+#define MAYBE_testRegularPlusIncognito DISABLED_testRegularPlusIncognito
+#else
+#define MAYBE_testRegularPlusIncognito testRegularPlusIncognito
 #endif
-
+- (void)MAYBE_testRegularPlusIncognito {
   uint64_t originalClientID = [MetricsAppInterface UKMClientID];
 
   OpenNewIncognitoTab();
@@ -235,12 +236,13 @@
 }
 
 // Make sure opening a real tab after Incognito doesn't enable UKM.
-- (void)testIncognitoPlusRegular {
 #if defined(CHROME_EARL_GREY_1)
-  // TODO(crbug.com/1033726): EG1 Test fails on iOS 12.
-  EARL_GREY_TEST_DISABLED(@"EG1 Fails");
+// TODO(crbug.com/1033726): EG1 Test fails on iOS 12.
+#define MAYBE_testIncognitoPlusRegular DISABLED_testIncognitoPlusRegular
+#else
+#define MAYBE_testIncognitoPlusRegular testIncognitoPlusRegular
 #endif
-
+- (void)MAYBE_testIncognitoPlusRegular {
   uint64_t originalClientID = [MetricsAppInterface UKMClientID];
   [ChromeEarlGrey closeAllTabs];
   [ChromeEarlGrey waitForMainTabCount:0];
diff --git a/ios/chrome/browser/translate/translate_egtest.mm b/ios/chrome/browser/translate/translate_egtest.mm
index bf82a3e9..aef25e93 100644
--- a/ios/chrome/browser/translate/translate_egtest.mm
+++ b/ios/chrome/browser/translate/translate_egtest.mm
@@ -1207,10 +1207,8 @@
 
 // Tests that "Never Translate ..." is automatically triggered only for a
 // maximum number of times if refused by the user.
-- (void)testInfobarAutoNeverTranslateMaxTries {
-  // TODO(crbug.com/945118): Re-enable when fixed.
-  EARL_GREY_TEST_DISABLED(@"Test disabled.");
-
+// TODO(crbug.com/945118): Re-enable when fixed.
+- (void)DISABLED_testInfobarAutoNeverTranslateMaxTries {
   // Start the HTTP server.
   std::unique_ptr<web::DataResponseProvider> provider(new TestResponseProvider);
   web::test::SetUpHttpServer(std::move(provider));
diff --git a/ios/chrome/browser/ui/activity_services/activities/BUILD.gn b/ios/chrome/browser/ui/activity_services/activities/BUILD.gn
index e52bcde..2a73ce6 100644
--- a/ios/chrome/browser/ui/activity_services/activities/BUILD.gn
+++ b/ios/chrome/browser/ui/activity_services/activities/BUILD.gn
@@ -11,6 +11,8 @@
     "copy_activity.mm",
     "find_in_page_activity.h",
     "find_in_page_activity.mm",
+    "generate_qr_code_activity.h",
+    "generate_qr_code_activity.mm",
     "print_activity.h",
     "print_activity.mm",
     "reading_list_activity.h",
@@ -25,6 +27,7 @@
     "resources:activity_services_copy",
     "resources:activity_services_edit_bookmark",
     "resources:activity_services_find_in_page",
+    "resources:activity_services_generate_qr_code",
     "resources:activity_services_print",
     "resources:activity_services_read_later",
     "resources:activity_services_request_desktop_site",
diff --git a/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h b/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h
new file mode 100644
index 0000000..f270785
--- /dev/null
+++ b/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h
@@ -0,0 +1,31 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_ACTIVITY_SERVICES_ACTIVITIES_GENERATE_QR_CODE_ACTIVITY_H_
+#define IOS_CHROME_BROWSER_UI_ACTIVITY_SERVICES_ACTIVITIES_GENERATE_QR_CODE_ACTIVITY_H_
+
+#import <UIKit/UIKit.h>
+
+#import "ios/chrome/browser/ui/commands/qr_generation_commands.h"
+#include "url/gurl.h"
+
+// Activity that ends up showing a QR code for the given URL.
+@interface GenerateQrCodeActivity : UIActivity
+
+// Identifier for the generate QR code activity.
++ (NSString*)activityIdentifier;
+
+// Initializes the GenerateQrCodeActivity with the |activityURL| used to
+// generate the QR code, the |title| of the page at that URL, and a |dispatcher|
+// to handle the command.
+- (instancetype)initWithURL:(const GURL&)activityURL
+                      title:(NSString*)title
+                 dispatcher:(id<QRGenerationCommands>)dispatcher
+    NS_DESIGNATED_INITIALIZER;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+#endif  // IOS_CHROME_BROWSER_UI_ACTIVITY_SERVICES_ACTIVITIES_GENERATE_QR_CODE_ACTIVITY_H_
diff --git a/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.mm b/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.mm
new file mode 100644
index 0000000..6fbe273
--- /dev/null
+++ b/ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.mm
@@ -0,0 +1,76 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/activity_services/activities/generate_qr_code_activity.h"
+
+#include "base/metrics/user_metrics.h"
+#include "base/metrics/user_metrics_action.h"
+#include "ios/chrome/grit/ios_strings.h"
+#include "ui/base/l10n/l10n_util_mac.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+NSString* const kGenerateQrCodeActivityType =
+    @"com.google.chrome.GenerateQrCodeActivityType";
+}  // namespace
+
+@interface GenerateQrCodeActivity () {
+  GURL _activityURL;
+}
+
+@property(nonatomic, weak, readonly) NSString* title;
+@property(nonatomic, weak, readonly) id<QRGenerationCommands> dispatcher;
+
+@end
+
+@implementation GenerateQrCodeActivity
+
++ (NSString*)activityIdentifier {
+  return kGenerateQrCodeActivityType;
+}
+
+- (instancetype)initWithURL:(const GURL&)activityURL
+                      title:(NSString*)title
+                 dispatcher:(id<QRGenerationCommands>)dispatcher {
+  if (self = [super init]) {
+    _activityURL = activityURL;
+    _title = title;
+    _dispatcher = dispatcher;
+  }
+  return self;
+}
+
+#pragma mark - UIActivity
+
+- (NSString*)activityType {
+  return [[self class] activityIdentifier];
+}
+
+- (NSString*)activityTitle {
+  return l10n_util::GetNSString(IDS_IOS_SHARE_MENU_GENERATE_QR_CODE_ACTION);
+}
+
+- (UIImage*)activityImage {
+  return [UIImage imageNamed:@"activity_services_generate_qr_code"];
+}
+
+- (BOOL)canPerformWithActivityItems:(NSArray*)activityItems {
+  return YES;
+}
+
++ (UIActivityCategory)activityCategory {
+  return UIActivityCategoryAction;
+}
+
+- (void)performActivity {
+  [self.dispatcher
+      generateQRCode:[[GenerateQRCodeCommand alloc] initWithURL:_activityURL
+                                                          title:self.title]];
+  [self activityDidFinish:YES];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/activity_services/activities/resources/BUILD.gn b/ios/chrome/browser/ui/activity_services/activities/resources/BUILD.gn
index a5197be..970c4fb8 100644
--- a/ios/chrome/browser/ui/activity_services/activities/resources/BUILD.gn
+++ b/ios/chrome/browser/ui/activity_services/activities/resources/BUILD.gn
@@ -40,6 +40,15 @@
   ]
 }
 
+imageset("activity_services_generate_qr_code") {
+  sources = [
+    "activity_services_generate_qr_code.imageset/Contents.json",
+    "activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@2x.png",
+    "activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@3x.png",
+    "activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-76@2x.png",
+  ]
+}
+
 imageset("activity_services_print") {
   sources = [
     "activity_services_print.imageset/Contents.json",
diff --git a/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/Contents.json b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/Contents.json
new file mode 100644
index 0000000..150be4d4
--- /dev/null
+++ b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "filename" : "activity_services_generate_qr_code-60@2x.png",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "filename" : "activity_services_generate_qr_code-60@3x.png",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "filename" : "activity_services_generate_qr_code-76@2x.png",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
diff --git a/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@2x.png b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@2x.png
new file mode 100644
index 0000000..583c955
--- /dev/null
+++ b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@3x.png b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@3x.png
new file mode 100644
index 0000000..6bfcdf3
--- /dev/null
+++ b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-60@3x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-76@2x.png b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-76@2x.png
new file mode 100644
index 0000000..9512e6d
--- /dev/null
+++ b/ios/chrome/browser/ui/activity_services/activities/resources/activity_services_generate_qr_code.imageset/activity_services_generate_qr_code-76@2x.png
Binary files differ
diff --git a/ios/chrome/browser/ui/activity_services/activity_type_util.h b/ios/chrome/browser/ui/activity_services/activity_type_util.h
index e1cdc24e..7c729c7c 100644
--- a/ios/chrome/browser/ui/activity_services/activity_type_util.h
+++ b/ios/chrome/browser/ui/activity_services/activity_type_util.h
@@ -43,6 +43,7 @@
   THIRD_PARTY_INSTAPAPER,
   APPEX_PASSWORD_MANAGEMENT,
   SEND_TAB_TO_SELF,
+  GENERATE_QR_CODE,
   // UNKNOWN must be the last type.
   UNKNOWN,
 };
diff --git a/ios/chrome/browser/ui/activity_services/activity_type_util.mm b/ios/chrome/browser/ui/activity_services/activity_type_util.mm
index c57630b..5803c44 100644
--- a/ios/chrome/browser/ui/activity_services/activity_type_util.mm
+++ b/ios/chrome/browser/ui/activity_services/activity_type_util.mm
@@ -81,6 +81,7 @@
     {NATIVE_CLIPBOARD, @"com.apple.UIKit.activity.CopyToPasteboard", true},
     {PRINT, @"com.google.chrome.printActivity", true},
     {FIND_IN_PAGE, @"com.google.chrome.FindInPageActivityType", true},
+    {GENERATE_QR_CODE, @"com.google.chrome.GenerateQrCodeActivityType", true},
     // The trailing '.' prevents false positives.
     // For instance, "com.viberific" won't be matched by the "com.viber.".
     {GOOGLE_DRIVE, @"com.google.Drive.", false},
@@ -229,6 +230,10 @@
       base::RecordAction(
           base::UserMetricsAction("MobileShareMenuSendTabToSelf"));
       break;
+    case GENERATE_QR_CODE:
+      base::RecordAction(
+          base::UserMetricsAction("MobileShareMenuGenerateQRCode"));
+      break;
   }
 }
 
diff --git a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
index e1c67d9..6dc7e9b 100644
--- a/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
+++ b/ios/chrome/browser/ui/bookmarks/bookmarks_egtest.mm
@@ -240,18 +240,19 @@
 }
 
 // Test that swiping left to right navigate back.
-- (void)testNavigateBackWithGesture {
+// TODO(crbug.com/768339): This test is faling on devices because
+// grey_swipeFastInDirectionWithStartPoint does not work.
+#if !TARGET_IPHONE_SIMULATOR
+#define MAYBE_testNavigateBackWithGesture DISABLED_testNavigateBackWithGesture
+#else
+#define MAYBE_testNavigateBackWithGesture testNavigateBackWithGesture
+#endif
+- (void)MAYBE_testNavigateBackWithGesture {
   // Disabled on iPad as there is not "navigate back" gesture.
   if ([ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_SKIPPED(@"Test not applicable for iPad");
   }
 
-// TODO(crbug.com/768339): This test is faling on devices because
-// grey_swipeFastInDirectionWithStartPoint does not work.
-#if !TARGET_IPHONE_SIMULATOR
-  EARL_GREY_TEST_DISABLED(@"Test disabled on devices.");
-#endif
-
   if (@available(iOS 13, *)) {
     // Navigate back side swipe gesture does not work on iOS13 simulator. This
     // is not specific to Bookmarks. The issue is that the gesture needs to
diff --git a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
index e53c53d..5cbd472 100644
--- a/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
+++ b/ios/chrome/browser/ui/dialogs/javascript_dialog_egtest.mm
@@ -532,11 +532,9 @@
 }
 
 // Tests that an alert is presented after displaying the share menu.
-- (void)testShowJavaScriptAfterShareMenu {
-  // TODO(crbug.com/747622): re-enable this test once earl grey can interact
-  // with the share menu.
-  EARL_GREY_TEST_DISABLED(@"Disabled until EG can use share menu.");
-
+// TODO(crbug.com/747622): re-enable this test once earl grey can interact
+// with the share menu.
+- (void)DISABLED_testShowJavaScriptAfterShareMenu {
   // Load the blank test page.
   const GURL kURL = self.testServer->GetURL(kAlertURLPath);
   [ChromeEarlGrey loadURL:kURL];
diff --git a/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm b/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm
index 6a6b922..30efebe 100644
--- a/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm
+++ b/ios/chrome/browser/ui/fullscreen/fullscreen_egtest.mm
@@ -116,11 +116,14 @@
 
 // Verifies that the toolbar is not hidden when scrolling a short pdf, as the
 // entire document is visible without hiding the toolbar.
-- (void)testSmallWidePDFScroll {
-// TODO(crbug.com/1022029): Enable this test.
 #if defined(CHROME_EARL_GREY_2)
-  EARL_GREY_TEST_DISABLED(@"Fails with EG2");
-#elif defined(CHROME_EARL_GREY_1)
+// TODO(crbug.com/1022029): Enable this test.
+#define MAYBE_testSmallWidePDFScroll DISABLED_testSmallWidePDFScroll
+#else
+#define MAYBE_testSmallWidePDFScroll testSmallWidePDFScroll
+#endif
+- (void)MAYBE_testSmallWidePDFScroll {
+#if defined(CHROME_EARL_GREY_1)
   // TODO(crbug.com/1036221): EG1 Test fails on iOS 12.
   if (!base::ios::IsRunningOnIOS13OrLater()) {
     EARL_GREY_TEST_DISABLED(@"EG1 Fails on iOS 12.");
@@ -153,12 +156,13 @@
 
 // Verifies that the toolbar properly appears/disappears when scrolling up/down
 // on a PDF that is long in length and wide in width.
-- (void)testLongPDFScroll {
-// TODO(crbug.com/714329): Re-enable this test on devices.
 #if !TARGET_IPHONE_SIMULATOR
-  EARL_GREY_TEST_DISABLED(@"Test disabled on device.");
+// TODO(crbug.com/714329): Re-enable this test on devices.
+#define MAYBE_testLongPDFScroll DISABLED_testLongPDFScroll
+#else
+#define MAYBE_testLongPDFScroll testLongPDFScroll
 #endif
-
+- (void)MAYBE_testLongPDFScroll {
   web::test::SetUpFileBasedHttpServer();
   GURL URL = web::test::HttpServer::MakeUrl(
       "http://ios/testing/data/http_server_files/two_pages.pdf");
diff --git a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
index 7806504..9198648 100644
--- a/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
+++ b/ios/chrome/browser/ui/omnibox/omnibox_egtest.mm
@@ -150,10 +150,13 @@
 
 // Tests that the XClientData header is sent when navigating to
 // https://google.com through the omnibox.
-- (void)testXClientData {
 #if defined(CHROME_EARL_GREY_1)
-  EARL_GREY_TEST_DISABLED(@"Flaky on EG1.");
+//  Flaky on EG1.
+#define MAYBE_testXClientData DISABLED_testXClientData
+#else
+#define MAYBE_testXClientData testXClientData
 #endif
+- (void)MAYBE_testXClientData {
   // Rewrite the google URL to localhost URL.
   [OmniboxAppInterface rewriteGoogleURLToLocalhost];
 
diff --git a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
index 90097e1..dd869a5 100644
--- a/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
+++ b/ios/chrome/browser/ui/settings/google_services/accounts_table_egtest.mm
@@ -258,11 +258,14 @@
 
 // Tests that the user isn't signed out and the UI is correct when the
 // disconnect is cancelled in the Account Settings screen.
-- (void)testSignInDisconnectCancelled {
-// TODO(crbug.com/669613): Re-enable this test on devices.
 #if !TARGET_IPHONE_SIMULATOR
-  EARL_GREY_TEST_DISABLED(@"Test disabled on device.");
+// TODO(crbug.com/669613): Re-enable this test on devices.
+#define MAYBE_testSignInDisconnectCancelled \
+  DISABLED_testSignInDisconnectCancelled
+#else
+#define MAYBE_testSignInDisconnectCancelled testSignInDisconnectCancelled
 #endif
+- (void)MAYBE_testSignInDisconnectCancelled {
   FakeChromeIdentity* fakeIdentity = [SigninEarlGreyUtils fakeIdentity1];
 
   // Sign In |fakeIdentity|, then open the Account Settings.
diff --git a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
index 5abb315..9e5844f 100644
--- a/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
+++ b/ios/chrome/browser/ui/toolbar/toolbar_egtest.mm
@@ -133,16 +133,14 @@
 // Tests whether input mode in an omnibox can be canceled via tapping the typing
 // shield and asserts it doesn't commit the omnibox contents if the input is
 // canceled.
-- (void)testToolbarOmniboxTypingShield {
+// TODO(crbug.com/753098): Re-enable this test on iPad once grey_typeText
+// works on iOS 11.
+- (void)DISABLED_testToolbarOmniboxTypingShield {
   // Tablet only (handset keyboard does not have "hide keyboard" button).
   if (![ChromeEarlGrey isIPadIdiom]) {
     EARL_GREY_TEST_SKIPPED(@"Test not support on iPhone");
   }
 
-  // TODO(crbug.com/753098): Re-enable this test on iPad once grey_typeText
-  // works on iOS 11.
-  EARL_GREY_TEST_DISABLED(@"Test disabled on iPad.");
-
   const GURL URL = web::test::HttpServer::MakeUrl("http://origin");
 
   [ChromeEarlGrey loadURL:URL];
@@ -203,11 +201,9 @@
 }
 
 // Verifies that copying and pasting a URL includes the hidden protocol prefix.
-- (void)testCopyPasteURL {
-  // TODO(crbug.com/834345): Enable this test when long press on the steady
-  // location bar is supported.
-  EARL_GREY_TEST_SKIPPED(@"Test not supported yet in UI Refresh.");
-
+// TODO(crbug.com/834345): Enable this test when long press on the steady
+// location bar is supported.
+- (void)DISABLED_testCopyPasteURL {
   // Clear generalPasteboard before and after the test.
   [UIPasteboard generalPasteboard].string = @"";
   [self setTearDownHandler:^{
diff --git a/ios/chrome/browser/ui/webui/BUILD.gn b/ios/chrome/browser/ui/webui/BUILD.gn
index 5b307b1..8f59dce 100644
--- a/ios/chrome/browser/ui/webui/BUILD.gn
+++ b/ios/chrome/browser/ui/webui/BUILD.gn
@@ -90,6 +90,7 @@
   sources = [
     "chrome_web_ui_ios_controller_factory.h",
     "chrome_web_ui_ios_controller_factory.mm",
+    "chrome_web_ui_provider.cc",
     "omaha_ui.cc",
     "omaha_ui.h",
     "signin_internals_ui_ios.cc",
@@ -105,11 +106,15 @@
     "//ios/chrome/browser/omaha",
     "//ios/chrome/browser/policy:feature_flags",
     "//ios/chrome/browser/signin",
+    "//ios/chrome/browser/sync",
     "//ios/chrome/browser/ui/webui/gcm",
+    "//ios/chrome/browser/ui/webui/interstitials",
     "//ios/chrome/browser/ui/webui/net_export",
     "//ios/chrome/browser/ui/webui/policy",
-    "//ios/chrome/browser/ui/webui/sync_internals",
     "//ios/chrome/browser/ui/webui/translate_internals",
+    "//ios/chrome/common",
+    "//ios/components/webui:provider",
+    "//ios/components/webui/sync_internals",
     "//url",
   ]
 
diff --git a/ios/chrome/browser/ui/webui/DEPS b/ios/chrome/browser/ui/webui/DEPS
index ef8ad28..f6641aa 100644
--- a/ios/chrome/browser/ui/webui/DEPS
+++ b/ios/chrome/browser/ui/webui/DEPS
@@ -1,3 +1,4 @@
 include_rules = [
   "+mojo/public",
+  "+ios/components/webui",
 ]
diff --git a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
index a2f30484..b5eb61f 100644
--- a/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
+++ b/ios/chrome/browser/ui/webui/chrome_web_ui_ios_controller_factory.mm
@@ -18,6 +18,7 @@
 #include "ios/chrome/browser/ui/webui/flags_ui.h"
 #include "ios/chrome/browser/ui/webui/gcm/gcm_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/inspect/inspect_ui.h"
+#include "ios/chrome/browser/ui/webui/interstitials/interstitial_ui.h"
 #include "ios/chrome/browser/ui/webui/net_export/net_export_ui.h"
 #include "ios/chrome/browser/ui/webui/ntp_tiles_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/omaha_ui.h"
@@ -25,12 +26,12 @@
 #include "ios/chrome/browser/ui/webui/prefs_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/signin_internals_ui_ios.h"
 #include "ios/chrome/browser/ui/webui/suggestions_ui.h"
-#include "ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/terms_ui.h"
 #include "ios/chrome/browser/ui/webui/translate_internals/translate_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/ukm_internals_ui.h"
 #include "ios/chrome/browser/ui/webui/user_actions_ui.h"
 #include "ios/chrome/browser/ui/webui/version_ui.h"
+#include "ios/components/webui/sync_internals/sync_internals_ui.h"
 #include "url/gurl.h"
 
 #if !defined(__has_feature) || !__has_feature(objc_arc)
@@ -84,6 +85,8 @@
     return &NewWebUIIOS<GCMInternalsUI>;
   if (url_host == kChromeUIInspectHost)
     return &NewWebUIIOS<InspectUI>;
+  if (url_host == kChromeUIIntersitialsHost)
+    return &NewWebUIIOS<InterstitialUI>;
   if (url_host == kChromeUINetExportHost)
     return &NewWebUIIOS<NetExportUI>;
   if (url_host == kChromeUINTPTilesInternalsHost)
@@ -105,7 +108,7 @@
   if (url_host == kChromeUIUserActionsHost)
     return &NewWebUIIOS<UserActionsUI>;
   if (url_host == kChromeUISyncInternalsHost)
-    return &NewWebUIIOS<SyncInternalsUI>;
+    return &NewWebUIIOSWithHost<SyncInternalsUI>;
   if (url_host == kChromeUITermsHost)
     return &NewWebUIIOSWithHost<TermsUI>;
   if (url_host == kChromeUIVersionHost)
diff --git a/ios/chrome/browser/ui/webui/chrome_web_ui_provider.cc b/ios/chrome/browser/ui/webui/chrome_web_ui_provider.cc
new file mode 100644
index 0000000..c78e90e
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/chrome_web_ui_provider.cc
@@ -0,0 +1,23 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ios/components/webui/web_ui_provider.h"
+
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
+#include "ios/chrome/common/channel_info.h"
+
+namespace web_ui {
+
+syncer::SyncService* GetSyncServiceForWebUI(web::WebUIIOS* web_ui) {
+  ChromeBrowserState* browser_state = ChromeBrowserState::FromWebUIIOS(web_ui);
+  return ProfileSyncServiceFactory::GetForBrowserState(
+      browser_state->GetOriginalChromeBrowserState());
+}
+
+version_info::Channel GetChannel() {
+  return ::GetChannel();
+}
+
+}  // namespace web_ui
diff --git a/ios/chrome/browser/ui/webui/interstitials/BUILD.gn b/ios/chrome/browser/ui/webui/interstitials/BUILD.gn
new file mode 100644
index 0000000..e413a4a
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/BUILD.gn
@@ -0,0 +1,80 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("interstitials") {
+  public = [ "interstitial_ui.h" ]
+  sources = [
+    "interstitial_ui.mm",
+    "interstitial_ui_util.h",
+    "interstitial_ui_util.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+
+  deps = [
+    ":constants",
+    "//base",
+    "//components/resources",
+    "//components/security_interstitials/core",
+    "//ios/chrome/browser:chrome_url_constants",
+    "//ios/chrome/browser/browser_state",
+    "//ios/chrome/browser/ssl",
+    "//ios/web/public/security",
+    "//ios/web/public/webui",
+    "//net",
+    "//ui/base",
+    "//url",
+  ]
+}
+
+source_set("constants") {
+  sources = [
+    "interstitial_ui_constants.h",
+    "interstitial_ui_constants.mm",
+  ]
+
+  configs += [ "//build/config/compiler:enable_arc" ]
+}
+
+source_set("eg_tests") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  defines = [ "CHROME_EARL_GREY_1" ]
+  testonly = true
+  sources = [ "interstitial_ui_egtest.mm" ]
+  deps = [
+    ":constants",
+    "//base",
+    "//base/test:test_support",
+    "//ios/chrome/browser:chrome_url_constants",
+    "//ios/chrome/test/app:test_support",
+    "//ios/chrome/test/earl_grey:test_support",
+    "//ios/testing/earl_grey:earl_grey_support",
+    "//net:test_support",
+    "//ui/base",
+  ]
+  libs = [ "XCTest.framework" ]
+}
+
+source_set("eg2_tests") {
+  defines = [ "CHROME_EARL_GREY_2" ]
+  configs += [
+    "//build/config/compiler:enable_arc",
+    "//build/config/ios:xctest_config",
+  ]
+  testonly = true
+  sources = [ "interstitial_ui_egtest.mm" ]
+  deps = [
+    ":constants",
+    "//base",
+    "//base/test:test_support",
+    "//ios/chrome/browser:chrome_url_constants",
+    "//ios/chrome/test/earl_grey:eg_test_support+eg2",
+    "//ios/testing/earl_grey:eg_test_support+eg2",
+    "//ios/third_party/earl_grey2:test_lib",
+    "//ios/web/public/test:element_selector",
+    "//net:test_support",
+    "//ui/base",
+  ]
+  libs = [ "UIKit.framework" ]
+}
diff --git a/ios/chrome/browser/ui/webui/interstitials/interstitial_ui.h b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui.h
new file mode 100644
index 0000000..f7bac71
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui.h
@@ -0,0 +1,19 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_H_
+
+#include "ios/web/public/webui/web_ui_ios_controller.h"
+
+// The WebUI handler for chrome://interstitials.
+class InterstitialUI : public web::WebUIIOSController {
+ public:
+  explicit InterstitialUI(web::WebUIIOS* web_ui);
+  ~InterstitialUI() override;
+  InterstitialUI(InterstitialUI&& other) = default;
+  InterstitialUI& operator=(InterstitialUI&& other) = default;
+};
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_H_
diff --git a/ios/chrome/browser/ui/webui/interstitials/interstitial_ui.mm b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui.mm
new file mode 100644
index 0000000..d097518
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui.mm
@@ -0,0 +1,115 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui.h"
+
+#import <Foundation/Foundation.h>
+#include <memory>
+#include <utility>
+
+#include "base/memory/ref_counted_memory.h"
+#include "base/time/time.h"
+#include "components/grit/dev_ui_components_resources.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.h"
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.h"
+#import "ios/web/public/security/web_interstitial_delegate.h"
+#import "ios/web/public/web_state.h"
+#include "ios/web/public/webui/url_data_source_ios.h"
+#include "ios/web/public/webui/web_ui_ios.h"
+#include "ios/web/public/webui/web_ui_ios_data_source.h"
+#include "net/base/url_util.h"
+#include "ui/base/resource/resource_bundle.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+// Implementation of chrome://interstitials demonstration pages.
+class InterstitialHTMLSource : public web::URLDataSourceIOS {
+ public:
+  explicit InterstitialHTMLSource(ChromeBrowserState* browser_state);
+  ~InterstitialHTMLSource() override;
+  InterstitialHTMLSource(InterstitialHTMLSource&& other) = default;
+  InterstitialHTMLSource& operator=(InterstitialHTMLSource&& other) = default;
+
+ private:
+  // web::URLDataSourceIOS:
+  std::string GetSource() const override;
+  void StartDataRequest(
+      const std::string& path,
+      web::URLDataSourceIOS::GotDataCallback callback) override;
+  std::string GetMimeType(const std::string& path) const override;
+
+  // The ChromeBrowserState passed on initialization.  Used to construct
+  // WebStates that are passed to WebInterstitialDelegates.
+  ChromeBrowserState* browser_state_ = nullptr;
+};
+
+}  //  namespace
+
+#pragma mark - InterstitialHTMLSource
+
+InterstitialHTMLSource::InterstitialHTMLSource(
+    ChromeBrowserState* browser_state)
+    : browser_state_(browser_state) {
+  DCHECK(browser_state_);
+}
+
+InterstitialHTMLSource::~InterstitialHTMLSource() = default;
+
+std::string InterstitialHTMLSource::GetMimeType(
+    const std::string& mime_type) const {
+  return "text/html";
+}
+
+std::string InterstitialHTMLSource::GetSource() const {
+  return kChromeUIIntersitialsHost;
+}
+
+void InterstitialHTMLSource::StartDataRequest(
+    const std::string& path,
+    web::URLDataSourceIOS::GotDataCallback callback) {
+  std::unique_ptr<web::WebState> web_state =
+      web::WebState::Create(web::WebState::CreateParams(browser_state_));
+  std::unique_ptr<web::WebInterstitialDelegate> interstitial_delegate;
+  std::string html;
+  // Using this form of the path so we can do exact matching, while ignoring the
+  // query (everything after the ? character).
+  GURL url = GURL(kChromeUIIntersitialsURL).GetWithEmptyPath().Resolve(path);
+  std::string path_without_query = url.path();
+  if (path_without_query == kChromeInterstitialSslPath) {
+    interstitial_delegate = CreateSslBlockingPageDelegate(web_state.get(), url);
+  } else if (path_without_query == kChromeInterstitialCaptivePortalPath) {
+    interstitial_delegate =
+        CreateCaptivePortalBlockingPageDelegate(web_state.get());
+  }
+  // TODO(crbug.com/1064805): Update the page HTML when a link for an
+  // unsupported interstitial type is tapped.
+
+  // Use the HTML generated from the interstitial delegate if created
+  // successfully.  Otherwise, return the default chrome://interstitials HTML.
+  if (interstitial_delegate) {
+    html = interstitial_delegate->GetHtmlContents();
+  } else {
+    html = ui::ResourceBundle::GetSharedInstance().LoadDataResourceString(
+        IDR_SECURITY_INTERSTITIAL_UI_HTML);
+  }
+
+  std::move(callback).Run(base::RefCountedString::TakeString(&html));
+}
+
+#pragma mark - InterstitialUI
+
+InterstitialUI::InterstitialUI(web::WebUIIOS* web_ui)
+    : WebUIIOSController(web_ui) {
+  ChromeBrowserState* browser_state = ChromeBrowserState::FromWebUIIOS(web_ui);
+  web::URLDataSourceIOS::Add(browser_state,
+                             new InterstitialHTMLSource(browser_state));
+}
+
+InterstitialUI::~InterstitialUI() = default;
diff --git a/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.h b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.h
new file mode 100644
index 0000000..7291686f
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.h
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_CONSTANTS_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_CONSTANTS_H_
+
+// Paths used by chrome://interstitials.
+extern const char kChromeInterstitialSslPath[];
+extern const char kChromeInterstitialCaptivePortalPath[];
+
+// Query keys and values for chrome://interstitials/ssl
+extern const char kChromeInterstitialSslUrlQueryKey[];
+extern const char kChromeInterstitialSslOverridableQueryKey[];
+extern const char kChromeInterstitialSslStrictEnforcementQueryKey[];
+extern const char kChromeInterstitialSslTypeQueryKey[];
+extern const char kChromeInterstitialSslTypeHpkpFailureQueryValue[];
+extern const char kChromeInterstitialSslTypeCtFailureQueryValue[];
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_CONSTANTS_H_
diff --git a/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.mm b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.mm
new file mode 100644
index 0000000..90ff86e
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.mm
@@ -0,0 +1,20 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+const char kChromeInterstitialSslPath[] = "/ssl";
+const char kChromeInterstitialCaptivePortalPath[] = "/captiveportal";
+
+const char kChromeInterstitialSslUrlQueryKey[] = "url";
+const char kChromeInterstitialSslOverridableQueryKey[] = "overridable";
+const char kChromeInterstitialSslStrictEnforcementQueryKey[] =
+    "strict_enforcement";
+const char kChromeInterstitialSslTypeQueryKey[] = "type";
+const char kChromeInterstitialSslTypeHpkpFailureQueryValue[] = "hpkp_failure";
+const char kChromeInterstitialSslTypeCtFailureQueryValue[] = "ct_failure";
diff --git a/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_egtest.mm b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_egtest.mm
new file mode 100644
index 0000000..d492b09
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_egtest.mm
@@ -0,0 +1,54 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <XCTest/XCTest.h>
+
+#include "ios/chrome/browser/chrome_url_constants.h"
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey.h"
+#import "ios/chrome/test/earl_grey/chrome_earl_grey_ui.h"
+#import "ios/chrome/test/earl_grey/chrome_matchers.h"
+#import "ios/chrome/test/earl_grey/chrome_test_case.h"
+#import "ios/testing/earl_grey/earl_grey_test.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+#include "ui/base/l10n/l10n_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+// Test case for chrome://interstitials WebUI page.
+@interface InterstitialWebUITestCase : ChromeTestCase
+@end
+
+@implementation InterstitialWebUITestCase
+
+// Tests that chrome://interstitials loads correctly.
+- (void)testLoadInterstitialUI {
+  [ChromeEarlGrey loadURL:GURL(kChromeUIIntersitialsURL)];
+
+  [ChromeEarlGrey waitForWebStateContainingText:"Choose an interstitial"];
+}
+
+// Tests that chrome://interstitials/ssl loads correctly.
+- (void)testLoadSSLInterstitialUI {
+  GURL SSLInterstitialURL =
+      GURL(kChromeUIIntersitialsURL).Resolve(kChromeInterstitialSslPath);
+  [ChromeEarlGrey loadURL:SSLInterstitialURL];
+
+  [ChromeEarlGrey
+      waitForWebStateContainingText:"Your connection is not private"];
+}
+
+// Tests that chrome://interstitials/captiveportal loads correctly.
+- (void)testLoadCaptivePortalInterstitialUI {
+  GURL captivePortalInterstitialURL =
+      GURL(kChromeUIIntersitialsURL)
+          .Resolve(kChromeInterstitialCaptivePortalPath);
+  [ChromeEarlGrey loadURL:captivePortalInterstitialURL];
+
+  [ChromeEarlGrey waitForWebStateContainingText:"Connect to Wi-Fi"];
+}
+
+@end
diff --git a/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.h b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.h
new file mode 100644
index 0000000..0e7990e
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.h
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_UTIL_H_
+#define IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_UTIL_H_
+
+#include <memory>
+
+class GURL;
+namespace web {
+class WebInterstitialDelegate;
+class WebState;
+}  // namespace web
+
+// Creates an interstitial delegate for chrome://interstitials/ssl.
+std::unique_ptr<web::WebInterstitialDelegate> CreateSslBlockingPageDelegate(
+    web::WebState* web_state,
+    const GURL& url);
+
+// Creates an interstitial delegate for chrome://interstitials/captiveportal.
+std::unique_ptr<web::WebInterstitialDelegate>
+CreateCaptivePortalBlockingPageDelegate(web::WebState* web_state);
+
+#endif  // IOS_CHROME_BROWSER_UI_WEBUI_INTERSTITIALS_INTERSTITIAL_UI_UTIL_H_
diff --git a/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.mm b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.mm
new file mode 100644
index 0000000..ec6a7878
--- /dev/null
+++ b/ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.mm
@@ -0,0 +1,121 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.h"
+
+#include "base/atomic_sequence_num.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted_memory.h"
+#include "base/time/time.h"
+#include "components/grit/dev_ui_components_resources.h"
+#include "components/security_interstitials/core/ssl_error_options_mask.h"
+#include "crypto/rsa_private_key.h"
+#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
+#include "ios/chrome/browser/chrome_url_constants.h"
+#include "ios/chrome/browser/ssl/ios_captive_portal_blocking_page.h"
+#include "ios/chrome/browser/ssl/ios_ssl_blocking_page.h"
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui_constants.h"
+#import "ios/chrome/browser/ui/webui/interstitials/interstitial_ui_util.h"
+#import "ios/web/public/security/web_interstitial_delegate.h"
+#include "ios/web/public/webui/url_data_source_ios.h"
+#include "ios/web/public/webui/web_ui_ios.h"
+#include "ios/web/public/webui/web_ui_ios_data_source.h"
+#include "net/base/url_util.h"
+#include "net/cert/x509_certificate.h"
+#include "net/cert/x509_util.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+scoped_refptr<net::X509Certificate> CreateFakeCert() {
+  // NSS requires that serial numbers be unique even for the same issuer;
+  // as all fake certificates will contain the same issuer name, it's
+  // necessary to ensure the serial number is unique, as otherwise
+  // NSS will fail to parse.
+  static base::AtomicSequenceNumber serial_number;
+
+  std::unique_ptr<crypto::RSAPrivateKey> unused_key;
+  std::string cert_der;
+  if (!net::x509_util::CreateKeyAndSelfSignedCert(
+          "CN=Error", static_cast<uint32_t>(serial_number.GetNext()),
+          base::Time::Now() - base::TimeDelta::FromMinutes(5),
+          base::Time::Now() + base::TimeDelta::FromMinutes(5), &unused_key,
+          &cert_der)) {
+    return nullptr;
+  }
+
+  return net::X509Certificate::CreateFromBytes(cert_der.data(),
+                                               cert_der.size());
+}
+
+}
+
+std::unique_ptr<web::WebInterstitialDelegate> CreateSslBlockingPageDelegate(
+    web::WebState* web_state,
+    const GURL& url) {
+  DCHECK_EQ(kChromeInterstitialSslPath, url.path());
+  // Fake parameters for SSL blocking page.
+  GURL request_url("https://example.com");
+  std::string url_param;
+  if (net::GetValueForKeyInQuery(url, kChromeInterstitialSslUrlQueryKey,
+                                 &url_param)) {
+    GURL query_url_param(url_param);
+    if (query_url_param.is_valid())
+      request_url = query_url_param;
+  }
+
+  bool overridable = false;
+  std::string overridable_param;
+  if (net::GetValueForKeyInQuery(url, kChromeInterstitialSslOverridableQueryKey,
+                                 &overridable_param)) {
+    overridable = overridable_param == "1";
+  }
+
+  bool strict_enforcement = false;
+  std::string strict_enforcement_param;
+  if (net::GetValueForKeyInQuery(
+          url, kChromeInterstitialSslStrictEnforcementQueryKey,
+          &strict_enforcement_param)) {
+    strict_enforcement = strict_enforcement_param == "1";
+  }
+
+  int cert_error = net::ERR_CERT_CONTAINS_ERRORS;
+  std::string type_param;
+  if (net::GetValueForKeyInQuery(url, kChromeInterstitialSslTypeQueryKey,
+                                 &type_param)) {
+    if (type_param == kChromeInterstitialSslTypeHpkpFailureQueryValue) {
+      cert_error = net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN;
+    } else if (type_param == kChromeInterstitialSslTypeCtFailureQueryValue) {
+      cert_error = net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED;
+    }
+  }
+
+  net::SSLInfo ssl_info;
+  ssl_info.cert = ssl_info.unverified_cert = CreateFakeCert();
+
+  int options_mask = 0;
+  if (overridable) {
+    options_mask |=
+        security_interstitials::SSLErrorOptionsMask::SOFT_OVERRIDE_ENABLED;
+  }
+  if (strict_enforcement) {
+    options_mask |=
+        security_interstitials::SSLErrorOptionsMask::STRICT_ENFORCEMENT;
+  }
+
+  return std::make_unique<IOSSSLBlockingPage>(
+      web_state, cert_error, ssl_info, request_url, options_mask,
+      base::Time::NowFromSystemTime(), base::OnceCallback<void(bool)>());
+}
+
+std::unique_ptr<web::WebInterstitialDelegate>
+CreateCaptivePortalBlockingPageDelegate(web::WebState* web_state) {
+  GURL landing_url("https://captive.portal/login");
+  GURL request_url("https://google.com");
+  return std::make_unique<IOSCaptivePortalBlockingPage>(
+      web_state, request_url, landing_url, base::OnceCallback<void(bool)>());
+}
diff --git a/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn b/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
deleted file mode 100644
index 67b18a2..0000000
--- a/ios/chrome/browser/ui/webui/sync_internals/BUILD.gn
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright 2016 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.
-
-source_set("sync_internals") {
-  sources = [
-    "sync_internals_message_handler.cc",
-    "sync_internals_message_handler.h",
-    "sync_internals_ui.cc",
-    "sync_internals_ui.h",
-  ]
-  deps = [
-    "//base",
-    "//components/browser_sync",
-    "//components/resources",
-    "//components/sync",
-    "//components/sync/driver:resources",
-    "//ios/chrome/browser",
-    "//ios/chrome/browser/browser_state",
-    "//ios/chrome/browser/signin",
-    "//ios/chrome/browser/sync",
-    "//ios/chrome/common",
-    "//ios/web/public/webui",
-  ]
-}
diff --git a/ios/chrome/common/credential_provider/BUILD.gn b/ios/chrome/common/credential_provider/BUILD.gn
index 76c4479..15b71edf 100644
--- a/ios/chrome/common/credential_provider/BUILD.gn
+++ b/ios/chrome/common/credential_provider/BUILD.gn
@@ -10,21 +10,32 @@
   sources = [
     "archivable_credential.h",
     "archivable_credential.mm",
+    "archivable_credential_store.h",
+    "archivable_credential_store.mm",
   ]
-  deps = [ ":ui" ]
+  deps = [
+    ":ui",
+    "//base",
+  ]
   libs = [ "Foundation.framework" ]
 }
 
 source_set("ui") {
   configs += [ "//build/config/compiler:enable_arc" ]
-  sources = [ "credential.h" ]
+  sources = [
+    "credential.h",
+    "credential_store.h",
+  ]
   libs = [ "Foundation.framework" ]
 }
 
 source_set("unit_tests") {
   configs += [ "//build/config/compiler:enable_arc" ]
   testonly = true
-  sources = [ "archivable_credential_unittest.mm" ]
+  sources = [
+    "archivable_credential_store_unittest.mm",
+    "archivable_credential_unittest.mm",
+  ]
   deps = [
     ":credential_provider",
     "//base/test:test_support",
diff --git a/ios/chrome/common/credential_provider/archivable_credential.h b/ios/chrome/common/credential_provider/archivable_credential.h
index 696f9cc3..2fcc778 100644
--- a/ios/chrome/common/credential_provider/archivable_credential.h
+++ b/ios/chrome/common/credential_provider/archivable_credential.h
@@ -9,6 +9,11 @@
 
 #import "ios/chrome/common/credential_provider/credential.h"
 
+// Credential that can be archived. |serviceIdentifier| must be unique between
+// credentials, as it is used for equality.
+//
+// Credentials are immutable and don't hold state, and because of this the
+// source of truth should always be the store.
 @interface ArchivableCredential : NSObject <Credential, NSSecureCoding>
 
 - (instancetype)initWithFavicon:(NSString*)favicon
diff --git a/ios/chrome/common/credential_provider/archivable_credential.mm b/ios/chrome/common/credential_provider/archivable_credential.mm
index 5228f39..94b80ee25 100644
--- a/ios/chrome/common/credential_provider/archivable_credential.mm
+++ b/ios/chrome/common/credential_provider/archivable_credential.mm
@@ -55,6 +55,32 @@
   return self;
 }
 
+- (BOOL)isEqual:(id)other {
+  if (other == self) {
+    return YES;
+  } else {
+    if (![other isKindOfClass:[ArchivableCredential class]]) {
+      return NO;
+    }
+    ArchivableCredential* otherCredential = (ArchivableCredential*)other;
+    return
+        [self.favicon isEqual:otherCredential.favicon] &&
+        [self.keychainIdentifier isEqual:otherCredential.keychainIdentifier] &&
+        self.rank == otherCredential.rank &&
+        [self.recordIdentifier isEqual:otherCredential.recordIdentifier] &&
+        [self.serviceIdentifier isEqual:otherCredential.serviceIdentifier] &&
+        [self.serviceName isEqual:otherCredential.serviceName] &&
+        [self.user isEqual:otherCredential.user] &&
+        [self.validationIdentifier
+            isEqual:otherCredential.validationIdentifier];
+  }
+}
+
+- (NSUInteger)hash {
+  // Using record identifier xored with keychain identifier should be enough.
+  return self.recordIdentifier.hash ^ self.keychainIdentifier.hash;
+}
+
 #pragma mark - NSSecureCoding
 
 + (BOOL)supportsSecureCoding {
diff --git a/ios/chrome/common/credential_provider/archivable_credential_store.h b/ios/chrome/common/credential_provider/archivable_credential_store.h
new file mode 100644
index 0000000..5088cb1
--- /dev/null
+++ b/ios/chrome/common/credential_provider/archivable_credential_store.h
@@ -0,0 +1,43 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_ARCHIVABLE_CREDENTIAL_STORE_H_
+#define IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_ARCHIVABLE_CREDENTIAL_STORE_H_
+
+#import <Foundation/Foundation.h>
+
+#import "ios/chrome/common/credential_provider/credential_store.h"
+
+@class ArchivableCredential;
+
+// Credential store built on top of keyed archiver and unarchiver to persist
+// data. Can also be used as a memory only store. Use |saveDataWithCompletion:|
+// to update the data on disk. All operations will be held in memory until saved
+// to disk, making it possible to batch multiple operations.
+@interface ArchivableCredentialStore : NSObject <CredentialStore>
+
+// Initializes the store. |fileURL| is where the store should live in disk. If
+// the file doesn't exist, it will be created on first save. If the file was not
+// created by this class the store won't be able to load it and the behavior
+// will be unexpected. In general |fileURL| can be a temp file for testing, or a
+// shared resource path in order to be used in an extension. Initialize the
+// store with |fileURL| = nil to effectively create a memory only store.
+- (instancetype)initWithFileURL:(NSURL*)fileURL NS_DESIGNATED_INITIALIZER;
+- (instancetype)init NS_UNAVAILABLE;
+
+// Adds a credential to the memory storage. Use |saveDataWithCompletion:| to
+// update the data on disk.
+- (void)addCredential:(ArchivableCredential*)credential;
+
+// Updates a credential in the memory storage. Use |saveDataWithCompletion:| to
+// update the data on disk.
+- (void)updateCredential:(ArchivableCredential*)credential;
+
+// Removes a credential from the memory storage. Use |saveDataWithCompletion:|
+// to update the data on disk.
+- (void)removeCredential:(ArchivableCredential*)credential;
+
+@end
+
+#endif  // IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_ARCHIVABLE_CREDENTIAL_STORE_H_
diff --git a/ios/chrome/common/credential_provider/archivable_credential_store.mm b/ios/chrome/common/credential_provider/archivable_credential_store.mm
new file mode 100644
index 0000000..4815553
--- /dev/null
+++ b/ios/chrome/common/credential_provider/archivable_credential_store.mm
@@ -0,0 +1,143 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/common/credential_provider/archivable_credential_store.h"
+
+#include "base/logging.h"
+#import "ios/chrome/common/credential_provider/archivable_credential.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+@interface ArchivableCredentialStore ()
+
+// Working queue used to sync the mutable set operations.
+@property(nonatomic) dispatch_queue_t workingQueue;
+
+// The fileURL to the disk file, can be nil.
+@property(nonatomic, strong) NSURL* fileURL;
+
+// The in-memory storage.
+@property(nonatomic, strong)
+    NSMutableDictionary<NSString*, ArchivableCredential*>* memoryStorage;
+
+@end
+
+@implementation ArchivableCredentialStore
+
+#pragma mark - Public
+
+- (instancetype)initWithFileURL:(NSURL*)fileURL {
+  self = [super init];
+  if (self) {
+    _fileURL = fileURL;
+    _workingQueue = dispatch_queue_create(nullptr, DISPATCH_QUEUE_CONCURRENT);
+  }
+  return self;
+}
+
+- (void)addCredential:(ArchivableCredential*)credential {
+  DCHECK(credential.recordIdentifier)
+      << "credential must have a record identifier";
+  dispatch_barrier_async(self.workingQueue, ^{
+    DCHECK(!self.memoryStorage[credential.recordIdentifier])
+        << "Credential already exists in the storage";
+    self.memoryStorage[credential.recordIdentifier] = credential;
+  });
+}
+
+- (void)updateCredential:(ArchivableCredential*)credential {
+  [self removeCredential:credential];
+  [self addCredential:credential];
+}
+
+- (void)removeCredential:(ArchivableCredential*)credential {
+  DCHECK(credential.recordIdentifier)
+      << "credential must have a record identifier";
+  dispatch_barrier_async(self.workingQueue, ^{
+    DCHECK(self.memoryStorage[credential.recordIdentifier])
+        << "Credential doesn't exist in the storage";
+    self.memoryStorage[credential.recordIdentifier] = nil;
+  });
+}
+
+#pragma mark - CredentialStore
+
+- (NSArray<id<Credential>>*)credentials {
+  __block NSArray<id<Credential>>* credentials;
+  dispatch_sync(self.workingQueue, ^{
+    credentials = [self.memoryStorage allValues];
+  });
+  return credentials;
+}
+
+- (void)saveDataWithCompletion:(void (^)(NSError* error))completion {
+  dispatch_barrier_async(self.workingQueue, ^{
+    if (!self.fileURL) {
+      return;
+    }
+    NSError* error = nil;
+    NSData* data =
+        [NSKeyedArchiver archivedDataWithRootObject:self.memoryStorage
+                              requiringSecureCoding:YES
+                                              error:&error];
+    DCHECK(!error) << error.debugDescription.UTF8String;
+    if (error) {
+      completion(error);
+      return;
+    }
+
+    [data writeToURL:self.fileURL options:NSDataWritingAtomic error:&error];
+    DCHECK(!error) << error.debugDescription.UTF8String;
+    completion(error);
+  });
+}
+
+#pragma mark - Getters
+
+- (NSMutableDictionary<NSString*, ArchivableCredential*>*)memoryStorage {
+#if !defined(NDEBUG)
+  dispatch_assert_queue(self.workingQueue);
+#endif  // !defined(NDEBUG)
+  if (!_memoryStorage) {
+    _memoryStorage = [self loadStorage];
+  }
+  return _memoryStorage;
+}
+
+#pragma mark - Private
+
+// Loads the store from disk.
+- (NSMutableDictionary<NSString*, ArchivableCredential*>*)loadStorage {
+#if !defined(NDEBUG)
+  dispatch_assert_queue(self.workingQueue);
+#endif  // !defined(NDEBUG)
+  if (!self.fileURL) {
+    return [[NSMutableDictionary alloc] init];
+  }
+  NSError* error = nil;
+  [self.fileURL checkResourceIsReachableAndReturnError:&error];
+  if (error) {
+    if (error.code == NSFileReadNoSuchFileError) {
+      // File has not been created, return a fresh mutable set.
+      return [[NSMutableDictionary alloc] init];
+    }
+    NOTREACHED();
+  }
+  NSData* data = [NSData dataWithContentsOfURL:self.fileURL
+                                       options:0
+                                         error:&error];
+  DCHECK(!error) << error.debugDescription.UTF8String;
+  NSSet* classes = [NSSet setWithObjects:[ArchivableCredential class],
+                                         [NSMutableDictionary class], nil];
+  NSMutableDictionary<NSString*, ArchivableCredential*>* dictionary =
+      [NSKeyedUnarchiver unarchivedObjectOfClasses:classes
+                                          fromData:data
+                                             error:&error];
+  DCHECK(!error) << error.debugDescription.UTF8String;
+  return dictionary;
+}
+
+@end
diff --git a/ios/chrome/common/credential_provider/archivable_credential_store_unittest.mm b/ios/chrome/common/credential_provider/archivable_credential_store_unittest.mm
new file mode 100644
index 0000000..50a88c17
--- /dev/null
+++ b/ios/chrome/common/credential_provider/archivable_credential_store_unittest.mm
@@ -0,0 +1,134 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "ios/chrome/common/credential_provider/archivable_credential_store.h"
+
+#import "base/test/ios/wait_util.h"
+#import "ios/chrome/common/credential_provider/archivable_credential.h"
+#include "testing/gtest_mac.h"
+#include "testing/platform_test.h"
+
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
+namespace {
+
+using base::test::ios::WaitUntilConditionOrTimeout;
+using base::test::ios::kWaitForFileOperationTimeout;
+
+NSURL* testStorageFileURL() {
+  NSURL* temporaryDirectory = [NSURL fileURLWithPath:NSTemporaryDirectory()];
+  NSURL* URL = [temporaryDirectory URLByAppendingPathComponent:@"credentials"];
+  return URL;
+}
+
+class ArchivableCredentialStoreTest : public PlatformTest {
+ protected:
+  void SetUp() override {
+    PlatformTest::SetUp();
+    [[NSFileManager defaultManager] removeItemAtURL:testStorageFileURL()
+                                              error:nil];
+  }
+  void TearDown() override {
+    PlatformTest::TearDown();
+    [[NSFileManager defaultManager] removeItemAtURL:testStorageFileURL()
+                                              error:nil];
+  }
+};
+
+ArchivableCredential* TestCredential() {
+  return [[ArchivableCredential alloc] initWithFavicon:@"favicon"
+                                    keychainIdentifier:@"keychainIdentifier"
+                                                  rank:5
+                                      recordIdentifier:@"recordIdentifier"
+                                     serviceIdentifier:@"serviceIdentifier"
+                                           serviceName:@"serviceName"
+                                                  user:@"user"
+                                  validationIdentifier:@"validationIdentifier"];
+}
+
+// Tests that an ArchivableCredentialStore can be created.
+TEST_F(ArchivableCredentialStoreTest, create) {
+  ArchivableCredentialStore* credentialStore =
+      [[ArchivableCredentialStore alloc] initWithFileURL:testStorageFileURL()];
+  EXPECT_TRUE(credentialStore);
+  EXPECT_TRUE(credentialStore.credentials);
+}
+
+// Tests that an ArchivableCredentialStore can add a credential.
+TEST_F(ArchivableCredentialStoreTest, add) {
+  ArchivableCredentialStore* credentialStore =
+      [[ArchivableCredentialStore alloc] initWithFileURL:testStorageFileURL()];
+  EXPECT_TRUE(credentialStore);
+  [credentialStore addCredential:TestCredential()];
+  EXPECT_EQ(1u, credentialStore.credentials.count);
+}
+
+// Tests that an ArchivableCredentialStore can update a credential.
+TEST_F(ArchivableCredentialStoreTest, update) {
+  ArchivableCredentialStore* credentialStore =
+      [[ArchivableCredentialStore alloc] initWithFileURL:testStorageFileURL()];
+  EXPECT_TRUE(credentialStore);
+  ArchivableCredential* credential = TestCredential();
+  [credentialStore addCredential:credential];
+  EXPECT_EQ(1u, credentialStore.credentials.count);
+
+  ArchivableCredential* updatedCredential = [[ArchivableCredential alloc]
+           initWithFavicon:@"other_favicon"
+        keychainIdentifier:@"other_keychainIdentifier"
+                      rank:credential.rank + 10
+          recordIdentifier:@"recordIdentifier"
+         serviceIdentifier:@"other_serviceIdentifier"
+               serviceName:@"other_serviceName"
+                      user:@"other_user"
+      validationIdentifier:@"other_validationIdentifier"];
+
+  [credentialStore updateCredential:updatedCredential];
+  EXPECT_EQ(1u, credentialStore.credentials.count);
+  EXPECT_EQ(updatedCredential.rank,
+            credentialStore.credentials.firstObject.rank);
+}
+
+// Tests that an ArchivableCredentialStore can remove a credential.
+TEST_F(ArchivableCredentialStoreTest, remove) {
+  ArchivableCredentialStore* credentialStore =
+      [[ArchivableCredentialStore alloc] initWithFileURL:testStorageFileURL()];
+  EXPECT_TRUE(credentialStore);
+  ArchivableCredential* credential = TestCredential();
+  [credentialStore addCredential:credential];
+  EXPECT_EQ(1u, credentialStore.credentials.count);
+
+  [credentialStore removeCredential:credential];
+  EXPECT_EQ(0u, credentialStore.credentials.count);
+}
+
+// Tests that ArchivableCredentialStore can save and retrieve from URLs.
+TEST_F(ArchivableCredentialStoreTest, persist) {
+  ArchivableCredentialStore* credentialStore =
+      [[ArchivableCredentialStore alloc] initWithFileURL:testStorageFileURL()];
+  EXPECT_TRUE(credentialStore);
+
+  ArchivableCredential* credential = TestCredential();
+  [credentialStore addCredential:credential];
+  EXPECT_EQ(1u, credentialStore.credentials.count);
+
+  __block BOOL blockWaitCompleted = false;
+  [credentialStore saveDataWithCompletion:^(NSError* error) {
+    EXPECT_FALSE(error);
+    blockWaitCompleted = true;
+  }];
+  EXPECT_TRUE(WaitUntilConditionOrTimeout(kWaitForFileOperationTimeout, ^bool {
+    return blockWaitCompleted;
+  }));
+
+  ArchivableCredentialStore* freshCredentialStore =
+      [[ArchivableCredentialStore alloc] initWithFileURL:testStorageFileURL()];
+  EXPECT_TRUE(freshCredentialStore);
+  EXPECT_TRUE(freshCredentialStore.credentials);
+  EXPECT_EQ(1u, freshCredentialStore.credentials.count);
+  EXPECT_TRUE(
+      [credential isEqual:freshCredentialStore.credentials.firstObject]);
+}
+}
diff --git a/ios/chrome/common/credential_provider/archivable_credential_unittest.mm b/ios/chrome/common/credential_provider/archivable_credential_unittest.mm
index d06c81d..ae78276 100644
--- a/ios/chrome/common/credential_provider/archivable_credential_unittest.mm
+++ b/ios/chrome/common/credential_provider/archivable_credential_unittest.mm
@@ -85,4 +85,35 @@
               unarchivedCredential.validationIdentifier);
 }
 
+// Tests ArchivableCredential equality.
+TEST_F(ArchivableCredentialTest, equality) {
+  ArchivableCredential* credential = TestCredential();
+  ArchivableCredential* credentialIdentical = TestCredential();
+  EXPECT_NSEQ(credential, credentialIdentical);
+  EXPECT_EQ(credential.hash, credentialIdentical.hash);
+
+  ArchivableCredential* credentialSameIdentifier = [[ArchivableCredential alloc]
+           initWithFavicon:@"other_favicon"
+        keychainIdentifier:@"other_keychainIdentifier"
+                      rank:credential.rank + 10
+          recordIdentifier:@"recordIdentifier"
+         serviceIdentifier:@"other_serviceIdentifier"
+               serviceName:@"other_serviceName"
+                      user:@"other_user"
+      validationIdentifier:@"other_validationIdentifier"];
+  EXPECT_NSNE(credential, credentialSameIdentifier);
+
+  ArchivableCredential* credentialDiferentIdentifier =
+      [[ArchivableCredential alloc] initWithFavicon:@"favicon"
+                                 keychainIdentifier:@"keychainIdentifier"
+                                               rank:credential.rank
+                                   recordIdentifier:@"other_recordIdentifier"
+                                  serviceIdentifier:@"serviceIdentifier"
+                                        serviceName:@"serviceName"
+                                               user:@"user"
+                               validationIdentifier:@"validationIdentifier"];
+  EXPECT_NSNE(credential, credentialDiferentIdentifier);
+
+  EXPECT_NSNE(credential, nil);
+}
 }
diff --git a/ios/chrome/common/credential_provider/credential_store.h b/ios/chrome/common/credential_provider/credential_store.h
new file mode 100644
index 0000000..6e1d436
--- /dev/null
+++ b/ios/chrome/common/credential_provider/credential_store.h
@@ -0,0 +1,25 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_CREDENTIAL_STORE_H_
+#define IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_CREDENTIAL_STORE_H_
+
+#import <Foundation/Foundation.h>
+
+@protocol Credential;
+
+// Manages the storage for credentials.
+@protocol CredentialStore
+
+// All the stored credentials.
+@property(nonatomic, readonly) NSArray<id<Credential>>* credentials;
+
+// When supported by the store save data to disk. |error| will be nil if the
+// save operation was successful, otherwise it will be the error generated by
+// the system.
+- (void)saveDataWithCompletion:(void (^)(NSError* error))completion;
+
+@end
+
+#endif  // IOS_CHROME_COMMON_CREDENTIAL_PROVIDER_CREDENTIAL_STORE_H_
diff --git a/ios/chrome/test/earl_grey/BUILD.gn b/ios/chrome/test/earl_grey/BUILD.gn
index 018be6a7..0b4ea45 100644
--- a/ios/chrome/test/earl_grey/BUILD.gn
+++ b/ios/chrome/test/earl_grey/BUILD.gn
@@ -108,6 +108,7 @@
     "//ios/chrome/browser/ui/tabs:eg_tests",
     "//ios/chrome/browser/ui/toolbar:eg_tests",
     "//ios/chrome/browser/ui/webui:eg_tests",
+    "//ios/chrome/browser/ui/webui/interstitials:eg_tests",
   ]
 
   shards = 2
diff --git a/ios/chrome/test/earl_grey2/BUILD.gn b/ios/chrome/test/earl_grey2/BUILD.gn
index 0ff1a55b..eaffe89 100644
--- a/ios/chrome/test/earl_grey2/BUILD.gn
+++ b/ios/chrome/test/earl_grey2/BUILD.gn
@@ -129,6 +129,7 @@
     "//ios/chrome/browser/ui/tabs:eg2_tests",
     "//ios/chrome/browser/ui/toolbar:eg2_tests",
     "//ios/chrome/browser/ui/webui:eg2_tests",
+    "//ios/chrome/browser/ui/webui/interstitials:eg2_tests",
   ]
   data_deps = [ ":ios_chrome_eg2tests" ]
 
diff --git a/ios/components/webui/BUILD.gn b/ios/components/webui/BUILD.gn
new file mode 100644
index 0000000..191b2183e
--- /dev/null
+++ b/ios/components/webui/BUILD.gn
@@ -0,0 +1,7 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("provider") {
+  sources = [ "web_ui_provider.h" ]
+}
diff --git a/ios/components/webui/sync_internals/BUILD.gn b/ios/components/webui/sync_internals/BUILD.gn
new file mode 100644
index 0000000..a725efe6
--- /dev/null
+++ b/ios/components/webui/sync_internals/BUILD.gn
@@ -0,0 +1,25 @@
+# Copyright 2020 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+source_set("sync_internals") {
+  configs += [ "//build/config/compiler:enable_arc" ]
+  sources = [
+    "sync_internals_message_handler.h",
+    "sync_internals_message_handler.mm",
+    "sync_internals_ui.h",
+    "sync_internals_ui.mm",
+  ]
+  deps = [
+    "//base",
+    "//components/browser_sync",
+    "//components/resources",
+    "//components/sync",
+    "//components/sync/driver:resources",
+    "//components/version_info:version_info",
+    "//ios/components/webui:provider",
+    "//ios/web/public",
+    "//ios/web/public/thread",
+    "//ios/web/public/webui",
+  ]
+}
diff --git a/ios/components/webui/sync_internals/DEPS b/ios/components/webui/sync_internals/DEPS
new file mode 100644
index 0000000..064a9dc
--- /dev/null
+++ b/ios/components/webui/sync_internals/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+  "+components/grit",
+  "+components/sync",
+]
diff --git a/ios/chrome/browser/ui/webui/sync_internals/OWNERS b/ios/components/webui/sync_internals/OWNERS
similarity index 100%
rename from ios/chrome/browser/ui/webui/sync_internals/OWNERS
rename to ios/components/webui/sync_internals/OWNERS
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h b/ios/components/webui/sync_internals/sync_internals_message_handler.h
similarity index 94%
rename from ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
rename to ios/components/webui/sync_internals/sync_internals_message_handler.h
index 7b69aed8..9f2ac70 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h
+++ b/ios/components/webui/sync_internals/sync_internals_message_handler.h
@@ -2,8 +2,8 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_MESSAGE_HANDLER_H_
-#define IOS_CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_MESSAGE_HANDLER_H_
+#ifndef IOS_COMPONENTS_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_MESSAGE_HANDLER_H_
+#define IOS_COMPONENTS_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_MESSAGE_HANDLER_H_
 
 #include <memory>
 #include <string>
@@ -126,4 +126,4 @@
   DISALLOW_COPY_AND_ASSIGN(SyncInternalsMessageHandler);
 };
 
-#endif  // IOS_CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_MESSAGE_HANDLER_H_
+#endif  // IOS_COMPONENTS_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_MESSAGE_HANDLER_H_
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc b/ios/components/webui/sync_internals/sync_internals_message_handler.mm
similarity index 94%
rename from ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
rename to ios/components/webui/sync_internals/sync_internals_message_handler.mm
index 3e193f9a..13089789 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.cc
+++ b/ios/components/webui/sync_internals/sync_internals_message_handler.mm
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h"
+#include "ios/components/webui/sync_internals/sync_internals_message_handler.h"
 
 #include <utility>
 #include <vector>
@@ -21,12 +21,14 @@
 #include "components/sync/engine/cycle/update_counters.h"
 #include "components/sync/engine/events/protocol_event.h"
 #include "components/sync/js/js_event_details.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/sync/profile_sync_service_factory.h"
-#include "ios/chrome/common/channel_info.h"
+#include "ios/components/webui/web_ui_provider.h"
 #include "ios/web/public/thread/web_thread.h"
 #include "ios/web/public/webui/web_ui_ios.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
 // Returns the initial state of the "include specifics" flag, based on whether
@@ -326,16 +328,13 @@
   syncer::SyncService* sync_service = GetSyncService();
   std::unique_ptr<base::DictionaryValue> value =
       syncer::sync_ui_util::ConstructAboutInformation(sync_service,
-                                                      GetChannel());
+                                                      web_ui::GetChannel());
   DispatchEvent(syncer::sync_ui_util::kOnAboutInfoUpdated, *value);
 }
 
 // Gets the SyncService of the underlying original profile. May return null.
 syncer::SyncService* SyncInternalsMessageHandler::GetSyncService() {
-  ChromeBrowserState* browser_state =
-      ChromeBrowserState::FromWebUIIOS(web_ui());
-  return ProfileSyncServiceFactory::GetForBrowserState(
-      browser_state->GetOriginalChromeBrowserState());
+  return web_ui::GetSyncServiceForWebUI(web_ui());
 }
 
 void SyncInternalsMessageHandler::DispatchEvent(
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.h b/ios/components/webui/sync_internals/sync_internals_ui.h
similarity index 63%
rename from ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.h
rename to ios/components/webui/sync_internals/sync_internals_ui.h
index 659d12d..a03bff8 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.h
+++ b/ios/components/webui/sync_internals/sync_internals_ui.h
@@ -2,8 +2,10 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifndef IOS_CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_UI_H_
-#define IOS_CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_UI_H_
+#ifndef IOS_COMPONENTS_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_UI_H_
+#define IOS_COMPONENTS_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_UI_H_
+
+#include <string>
 
 #include "base/macros.h"
 #include "ios/web/public/webui/web_ui_ios_controller.h"
@@ -15,11 +17,11 @@
 // The implementation for the chrome://sync-internals page.
 class SyncInternalsUI : public web::WebUIIOSController {
  public:
-  explicit SyncInternalsUI(web::WebUIIOS* web_ui);
+  explicit SyncInternalsUI(web::WebUIIOS* web_ui, const std::string& host);
   ~SyncInternalsUI() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SyncInternalsUI);
 };
 
-#endif  // IOS_CHROME_BROWSER_UI_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_UI_H_
+#endif  // IOS_COMPONENTS_WEBUI_SYNC_INTERNALS_SYNC_INTERNALS_UI_H_
diff --git a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc b/ios/components/webui/sync_internals/sync_internals_ui.mm
similarity index 75%
rename from ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
rename to ios/components/webui/sync_internals/sync_internals_ui.mm
index 27f6d0f..80e30630 100644
--- a/ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.cc
+++ b/ios/components/webui/sync_internals/sync_internals_ui.mm
@@ -2,23 +2,27 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "ios/chrome/browser/ui/webui/sync_internals/sync_internals_ui.h"
+#include "ios/components/webui/sync_internals/sync_internals_ui.h"
 
 #include <memory>
 
 #include "components/grit/sync_driver_resources.h"
 #include "components/sync/driver/about_sync_util.h"
-#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
-#include "ios/chrome/browser/chrome_url_constants.h"
-#include "ios/chrome/browser/ui/webui/sync_internals/sync_internals_message_handler.h"
+#include "ios/components/webui/sync_internals/sync_internals_message_handler.h"
+#include "ios/web/public/browser_state.h"
+#include "ios/web/public/web_state.h"
 #include "ios/web/public/webui/web_ui_ios.h"
 #include "ios/web/public/webui/web_ui_ios_data_source.h"
 
+#if !defined(__has_feature) || !__has_feature(objc_arc)
+#error "This file requires ARC support."
+#endif
+
 namespace {
 
-web::WebUIIOSDataSource* CreateSyncInternalsHTMLSource() {
-  web::WebUIIOSDataSource* source =
-      web::WebUIIOSDataSource::Create(kChromeUISyncInternalsHost);
+web::WebUIIOSDataSource* CreateSyncInternalsHTMLSource(
+    const std::string& host) {
+  web::WebUIIOSDataSource* source = web::WebUIIOSDataSource::Create(host);
 
   source->UseStringsJs();
   source->AddResourcePath(syncer::sync_ui_util::kSyncIndexJS,
@@ -51,10 +55,10 @@
 
 }  // namespace
 
-SyncInternalsUI::SyncInternalsUI(web::WebUIIOS* web_ui)
+SyncInternalsUI::SyncInternalsUI(web::WebUIIOS* web_ui, const std::string& host)
     : web::WebUIIOSController(web_ui) {
-  web::WebUIIOSDataSource::Add(ChromeBrowserState::FromWebUIIOS(web_ui),
-                               CreateSyncInternalsHTMLSource());
+  web::WebUIIOSDataSource::Add(web_ui->GetWebState()->GetBrowserState(),
+                               CreateSyncInternalsHTMLSource(host));
   web_ui->AddMessageHandler(std::make_unique<SyncInternalsMessageHandler>());
 }
 
diff --git a/ios/components/webui/web_ui_provider.h b/ios/components/webui/web_ui_provider.h
new file mode 100644
index 0000000..a04b7f4
--- /dev/null
+++ b/ios/components/webui/web_ui_provider.h
@@ -0,0 +1,32 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IOS_COMPONENTS_WEBUI_WEB_UI_PROVIDER_H_
+#define IOS_COMPONENTS_WEBUI_WEB_UI_PROVIDER_H_
+
+namespace syncer {
+class SyncService;
+}  // namespace syncer
+
+namespace version_info {
+enum class Channel;
+}  // namespace version_info
+
+namespace web {
+class WebUIIOS;
+}  // namespace web
+
+// Declares functions that must be implemented by the embedder, such as
+// ios/chrome and ios/web_view.
+namespace web_ui {
+
+// Gets the SyncService of the underlying original profile. May return null.
+syncer::SyncService* GetSyncServiceForWebUI(web::WebUIIOS* web_ui);
+
+// Returns the app channel .
+version_info::Channel GetChannel();
+
+}  // namespace web_ui
+
+#endif  // IOS_COMPONENTS_WEBUI_WEB_UI_PROVIDER_H_
diff --git a/ios/web_view/internal/web_view_global_state_util.mm b/ios/web_view/internal/web_view_global_state_util.mm
index 33381a7..1d9123c 100644
--- a/ios/web_view/internal/web_view_global_state_util.mm
+++ b/ios/web_view/internal/web_view_global_state_util.mm
@@ -18,13 +18,11 @@
 namespace ios_web_view {
 
 void InitializeGlobalState() {
-#if defined(UNIT_TEST)
   // Do not perform global state initialization in an unit test environment.
   // 1. Not needed when unit testing.
   // 2. The globals below will try to create other already created globals like
   //    AtExitManagers. This causes DCHECKs and prevents tests from completing.
-  return;
-#endif  // defined(UNIT_TEST)
+#if !defined(UNIT_TEST)
   static std::unique_ptr<ios_web_view::WebViewWebClient> web_client;
   static std::unique_ptr<ios_web_view::WebViewWebMainDelegate>
       web_main_delegate;
@@ -51,6 +49,7 @@
                   web_client.reset();
                 }];
   });
+#endif  // defined(UNIT_TEST)
 }
 
 }  // namespace ios_web_view
diff --git a/services/device/usb/usb_device_handle_win.cc b/services/device/usb/usb_device_handle_win.cc
index 35efed0..7ca3f84 100644
--- a/services/device/usb/usb_device_handle_win.cc
+++ b/services/device/usb/usb_device_handle_win.cc
@@ -332,7 +332,8 @@
                            base::Owned(node_connection_info), buffer));
         return;
       } else if (((value >> 8) == USB_CONFIGURATION_DESCRIPTOR_TYPE) ||
-                 ((value >> 8) == USB_STRING_DESCRIPTOR_TYPE)) {
+                 ((value >> 8) == USB_STRING_DESCRIPTOR_TYPE) ||
+                 ((value >> 8) == USB_BOS_DESCRIPTOR_TYPE)) {
         size_t size = sizeof(USB_DESCRIPTOR_REQUEST) + buffer->size();
         auto request_buffer = base::MakeRefCounted<base::RefCountedBytes>(size);
         USB_DESCRIPTOR_REQUEST* descriptor_request =
diff --git a/services/device/usb/usb_device_win.cc b/services/device/usb/usb_device_win.cc
index 09a5dd9..841d232 100644
--- a/services/device/usb/usb_device_win.cc
+++ b/services/device/usb/usb_device_win.cc
@@ -118,8 +118,6 @@
     std::unique_ptr<std::map<uint8_t, base::string16>> string_map) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
-  device_handle->Close();
-
   if (i_manufacturer)
     device_info_->manufacturer_name = (*string_map)[i_manufacturer];
   if (i_product)
@@ -128,15 +126,37 @@
     device_info_->serial_number = (*string_map)[i_serial_number];
 
   if (usb_version() >= kUsbVersion2_1) {
-    Open(base::BindOnce(&UsbDeviceWin::OnOpenedToReadWebUsbDescriptors, this,
-                        std::move(callback)));
+    ReadWebUsbCapabilityDescriptor(
+        device_handle,
+        base::BindOnce(&UsbDeviceWin::OnReadWebUsbCapabilityDescriptor, this,
+                       std::move(callback), device_handle));
   } else {
+    device_handle->Close();
     std::move(callback).Run(true);
   }
 }
 
-void UsbDeviceWin::OnOpenedToReadWebUsbDescriptors(
+void UsbDeviceWin::OnReadWebUsbCapabilityDescriptor(
     base::OnceCallback<void(bool)> callback,
+    scoped_refptr<UsbDeviceHandle> device_handle,
+    const base::Optional<WebUsbPlatformCapabilityDescriptor>& descriptor) {
+  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
+  device_handle->Close();
+
+  if (!descriptor || !descriptor->landing_page_id) {
+    std::move(callback).Run(true);
+    return;
+  }
+
+  Open(base::BindOnce(&UsbDeviceWin::OnOpenedToReadWebUsbLandingPage, this,
+                      std::move(callback), descriptor->vendor_code,
+                      descriptor->landing_page_id));
+}
+
+void UsbDeviceWin::OnOpenedToReadWebUsbLandingPage(
+    base::OnceCallback<void(bool)> callback,
+    uint8_t vendor_code,
+    uint8_t landing_page_id,
     scoped_refptr<UsbDeviceHandle> device_handle) {
   DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
 
@@ -147,12 +167,13 @@
     return;
   }
 
-  ReadWebUsbDescriptors(
-      device_handle, base::BindOnce(&UsbDeviceWin::OnReadWebUsbDescriptors,
-                                    this, std::move(callback), device_handle));
+  ReadWebUsbLandingPage(
+      vendor_code, landing_page_id, device_handle,
+      base::BindOnce(&UsbDeviceWin::OnReadWebUsbLandingPage, this,
+                     std::move(callback), device_handle));
 }
 
-void UsbDeviceWin::OnReadWebUsbDescriptors(
+void UsbDeviceWin::OnReadWebUsbLandingPage(
     base::OnceCallback<void(bool)> callback,
     scoped_refptr<UsbDeviceHandle> device_handle,
     const GURL& landing_page) {
diff --git a/services/device/usb/usb_device_win.h b/services/device/usb/usb_device_win.h
index 6cb5b2b..1d7f818 100644
--- a/services/device/usb/usb_device_win.h
+++ b/services/device/usb/usb_device_win.h
@@ -17,6 +17,7 @@
 namespace device {
 
 struct UsbDeviceDescriptor;
+struct WebUsbPlatformCapabilityDescriptor;
 
 class UsbDeviceWin : public UsbDevice {
  public:
@@ -54,10 +55,16 @@
       uint8_t i_product,
       uint8_t i_serial_number,
       std::unique_ptr<std::map<uint8_t, base::string16>> string_map);
-  void OnOpenedToReadWebUsbDescriptors(
+  void OnReadWebUsbCapabilityDescriptor(
       base::OnceCallback<void(bool)> callback,
+      scoped_refptr<UsbDeviceHandle> device_handle,
+      const base::Optional<WebUsbPlatformCapabilityDescriptor>& descriptor);
+  void OnOpenedToReadWebUsbLandingPage(
+      base::OnceCallback<void(bool)> callback,
+      uint8_t vendor_code,
+      uint8_t landing_page_id,
       scoped_refptr<UsbDeviceHandle> device_handle);
-  void OnReadWebUsbDescriptors(base::OnceCallback<void(bool)> callback,
+  void OnReadWebUsbLandingPage(base::OnceCallback<void(bool)> callback,
                                scoped_refptr<UsbDeviceHandle> device_handle,
                                const GURL& landing_page);
 
diff --git a/services/device/usb/webusb_descriptors.cc b/services/device/usb/webusb_descriptors.cc
index 9279974..1abc6e9 100644
--- a/services/device/usb/webusb_descriptors.cc
+++ b/services/device/usb/webusb_descriptors.cc
@@ -4,6 +4,8 @@
 
 #include "services/device/usb/webusb_descriptors.h"
 
+#include <limits>
+
 #include "base/bind.h"
 #include "base/callback.h"
 #include "base/logging.h"
@@ -11,6 +13,7 @@
 #include "base/stl_util.h"
 #include "components/device_event_log/device_event_log.h"
 #include "services/device/usb/usb_device_handle.h"
+#include "url/gurl.h"
 
 namespace device {
 
@@ -39,13 +42,16 @@
     0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47,
     0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65};
 
+const size_t kMaxControlTransferLength = std::numeric_limits<uint8_t>::max();
 const int kControlTransferTimeoutMs = 2000;  // 2 seconds
 
-using ReadWebUsbDescriptorsCallback =
+using ReadCompatabilityDescriptorCallback = base::OnceCallback<void(
+    const base::Optional<WebUsbPlatformCapabilityDescriptor>& descriptor)>;
+using ReadLandingPageCallback =
     base::OnceCallback<void(const GURL& landing_page)>;
 
 void OnReadLandingPage(uint8_t landing_page_id,
-                       ReadWebUsbDescriptorsCallback callback,
+                       ReadLandingPageCallback callback,
                        UsbTransferStatus status,
                        scoped_refptr<base::RefCountedBytes> buffer,
                        size_t length) {
@@ -62,52 +68,35 @@
   std::move(callback).Run(url);
 }
 
-void ReadLandingPage(uint8_t vendor_code,
-                     uint8_t landing_page_id,
-                     scoped_refptr<UsbDeviceHandle> device_handle,
-                     ReadWebUsbDescriptorsCallback callback) {
-  auto buffer = base::MakeRefCounted<base::RefCountedBytes>(255);
-  device_handle->ControlTransfer(
-      UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR,
-      UsbControlTransferRecipient::DEVICE, vendor_code, landing_page_id,
-      kGetUrlRequest, buffer, kControlTransferTimeoutMs,
-      base::BindOnce(&OnReadLandingPage, landing_page_id, std::move(callback)));
-}
-
 void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle,
-                         ReadWebUsbDescriptorsCallback callback,
+                         ReadCompatabilityDescriptorCallback callback,
                          UsbTransferStatus status,
                          scoped_refptr<base::RefCountedBytes> buffer,
                          size_t length) {
   if (status != UsbTransferStatus::COMPLETED) {
     USB_LOG(EVENT) << "Failed to read BOS descriptor.";
-    std::move(callback).Run(GURL());
+    std::move(callback).Run(base::nullopt);
     return;
   }
 
   WebUsbPlatformCapabilityDescriptor descriptor;
   if (!descriptor.ParseFromBosDescriptor(
           std::vector<uint8_t>(buffer->front(), buffer->front() + length))) {
-    std::move(callback).Run(GURL());
+    std::move(callback).Run(base::nullopt);
     return;
   }
 
-  if (descriptor.landing_page_id) {
-    ReadLandingPage(descriptor.vendor_code, descriptor.landing_page_id,
-                    device_handle, std::move(callback));
-  } else {
-    std::move(callback).Run(GURL());
-  }
+  std::move(callback).Run(descriptor);
 }
 
 void OnReadBosDescriptorHeader(scoped_refptr<UsbDeviceHandle> device_handle,
-                               ReadWebUsbDescriptorsCallback callback,
+                               ReadCompatabilityDescriptorCallback callback,
                                UsbTransferStatus status,
                                scoped_refptr<base::RefCountedBytes> buffer,
                                size_t length) {
   if (status != UsbTransferStatus::COMPLETED || length != 5) {
     USB_LOG(EVENT) << "Failed to read BOS descriptor header.";
-    std::move(callback).Run(GURL());
+    std::move(callback).Run(base::nullopt);
     return;
   }
 
@@ -121,6 +110,19 @@
       base::BindOnce(&OnReadBosDescriptor, device_handle, std::move(callback)));
 }
 
+void OnReadWebUsbCapabilityDescriptor(
+    scoped_refptr<UsbDeviceHandle> device_handle,
+    ReadLandingPageCallback callback,
+    const base::Optional<WebUsbPlatformCapabilityDescriptor>& descriptor) {
+  if (!descriptor || !descriptor->landing_page_id) {
+    std::move(callback).Run(GURL());
+    return;
+  }
+
+  ReadWebUsbLandingPage(descriptor->vendor_code, descriptor->landing_page_id,
+                        device_handle, std::move(callback));
+}
+
 }  // namespace
 
 WebUsbPlatformCapabilityDescriptor::WebUsbPlatformCapabilityDescriptor()
@@ -251,8 +253,22 @@
   return true;
 }
 
-void ReadWebUsbDescriptors(scoped_refptr<UsbDeviceHandle> device_handle,
-                           ReadWebUsbDescriptorsCallback callback) {
+void ReadWebUsbLandingPage(uint8_t vendor_code,
+                           uint8_t landing_page_id,
+                           scoped_refptr<UsbDeviceHandle> device_handle,
+                           ReadLandingPageCallback callback) {
+  auto buffer =
+      base::MakeRefCounted<base::RefCountedBytes>(kMaxControlTransferLength);
+  device_handle->ControlTransfer(
+      UsbTransferDirection::INBOUND, UsbControlTransferType::VENDOR,
+      UsbControlTransferRecipient::DEVICE, vendor_code, landing_page_id,
+      kGetUrlRequest, buffer, kControlTransferTimeoutMs,
+      base::BindOnce(&OnReadLandingPage, landing_page_id, std::move(callback)));
+}
+
+void ReadWebUsbCapabilityDescriptor(
+    scoped_refptr<UsbDeviceHandle> device_handle,
+    ReadCompatabilityDescriptorCallback callback) {
   auto buffer = base::MakeRefCounted<base::RefCountedBytes>(5);
   device_handle->ControlTransfer(
       UsbTransferDirection::INBOUND, UsbControlTransferType::STANDARD,
@@ -262,4 +278,11 @@
                      std::move(callback)));
 }
 
+void ReadWebUsbDescriptors(scoped_refptr<UsbDeviceHandle> device_handle,
+                           ReadLandingPageCallback callback) {
+  ReadWebUsbCapabilityDescriptor(
+      device_handle, base::BindOnce(&OnReadWebUsbCapabilityDescriptor,
+                                    device_handle, std::move(callback)));
+}
+
 }  // namespace device
diff --git a/services/device/usb/webusb_descriptors.h b/services/device/usb/webusb_descriptors.h
index 86f69e10..ac4f4a6 100644
--- a/services/device/usb/webusb_descriptors.h
+++ b/services/device/usb/webusb_descriptors.h
@@ -11,7 +11,9 @@
 
 #include "base/callback_forward.h"
 #include "base/memory/ref_counted.h"
-#include "url/gurl.h"
+#include "base/optional.h"
+
+class GURL;
 
 namespace device {
 
@@ -26,11 +28,22 @@
   uint16_t version;
   uint8_t vendor_code;
   uint8_t landing_page_id;
-  GURL landing_page;
 };
 
 bool ParseWebUsbUrlDescriptor(const std::vector<uint8_t>& bytes, GURL* output);
 
+void ReadWebUsbLandingPage(
+    uint8_t vendor_code,
+    uint8_t landing_page_id,
+    scoped_refptr<UsbDeviceHandle> device_handle,
+    base::OnceCallback<void(const GURL& landing_page)> callback);
+
+void ReadWebUsbCapabilityDescriptor(
+    scoped_refptr<UsbDeviceHandle> device_handle,
+    base::OnceCallback<void(
+        const base::Optional<WebUsbPlatformCapabilityDescriptor>& descriptor)>
+        callback);
+
 void ReadWebUsbDescriptors(
     scoped_refptr<UsbDeviceHandle> device_handle,
     base::OnceCallback<void(const GURL& landing_page)> callback);
diff --git a/testing/buildbot/chromium.ci.json b/testing/buildbot/chromium.ci.json
index f70e2ee..b2d6a737 100644
--- a/testing/buildbot/chromium.ci.json
+++ b/testing/buildbot/chromium.ci.json
@@ -44182,6 +44182,28 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "gpu_unittests",
+        "test_target": "//gpu:gpu_unittests"
+      },
+      {
+        "args": [
+          "--no-xvfb",
+          "--use-weston",
+          "--ozone-platform=wayland"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
         "test": "ui_touch_selection_unittests",
         "test_target": "//ui/touch_selection:ui_touch_selection_unittests"
       },
@@ -44250,6 +44272,26 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "gpu_unittests",
+        "test_target": "//gpu:gpu_unittests"
+      },
+      {
+        "args": [
+          "--ozone-platform=x11"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
         "test": "ozone_x11_unittests",
         "test_target": "//ui/ozone:ozone_x11_unittests"
       },
diff --git a/testing/buildbot/chromium.linux.json b/testing/buildbot/chromium.linux.json
index e822e5ce..ad1fca1 100644
--- a/testing/buildbot/chromium.linux.json
+++ b/testing/buildbot/chromium.linux.json
@@ -2703,6 +2703,28 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "gpu_unittests",
+        "test_target": "//gpu:gpu_unittests"
+      },
+      {
+        "args": [
+          "--no-xvfb",
+          "--use-weston",
+          "--ozone-platform=wayland"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
         "test": "ui_touch_selection_unittests",
         "test_target": "//ui/touch_selection:ui_touch_selection_unittests"
       },
@@ -2771,6 +2793,26 @@
           ],
           "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
         },
+        "test": "gpu_unittests",
+        "test_target": "//gpu:gpu_unittests"
+      },
+      {
+        "args": [
+          "--ozone-platform=x11"
+        ],
+        "merge": {
+          "args": [],
+          "script": "//testing/merge_scripts/standard_gtest_merge.py"
+        },
+        "swarming": {
+          "can_use_on_swarming_builders": true,
+          "dimension_sets": [
+            {
+              "os": "Ubuntu-16.04"
+            }
+          ],
+          "service_account": "chromium-tester@chops-service-accounts.iam.gserviceaccount.com"
+        },
         "test": "ozone_x11_unittests",
         "test_target": "//ui/ozone:ozone_x11_unittests"
       },
diff --git a/testing/buildbot/test_suites.pyl b/testing/buildbot/test_suites.pyl
index 1914923..5218666 100644
--- a/testing/buildbot/test_suites.pyl
+++ b/testing/buildbot/test_suites.pyl
@@ -3200,6 +3200,7 @@
     },
 
     'ozone_linux_gtests_wayland': {
+      'gpu_unittests': {},
       'ui_touch_selection_unittests': {},
       'wayland_views_unittests': {
         'args': [
@@ -3211,6 +3212,7 @@
 
     'ozone_linux_gtests_x11': {
       'content_unittests': {},
+      'gpu_unittests': {},
       'ozone_x11_unittests': {},
       'ui_touch_selection_unittests': {},
       'views_unittests': {
diff --git a/testing/merge_scripts/code_coverage/merge_lib.py b/testing/merge_scripts/code_coverage/merge_lib.py
index 03fa159..52b783f 100644
--- a/testing/merge_scripts/code_coverage/merge_lib.py
+++ b/testing/merge_scripts/code_coverage/merge_lib.py
@@ -6,6 +6,7 @@
 import logging
 import multiprocessing
 import os
+import re
 import shutil
 import subprocess
 
@@ -87,14 +88,16 @@
   return []
 
 
-def _get_profile_paths(input_dir, input_extension):
+def _get_profile_paths(input_dir,
+                       input_extension,
+                       input_filename_pattern='.*'):
   """Finds all the profiles in the given directory (recursively)."""
   paths = []
   for dir_path, _sub_dirs, file_names in os.walk(input_dir):
     paths.extend([
         os.path.join(dir_path, fn)
         for fn in file_names
-        if fn.endswith(input_extension)
+        if fn.endswith(input_extension) and re.search(input_filename_pattern,fn)
     ])
   return paths
 
@@ -224,7 +227,11 @@
   logging.info('Merge succeeded with output: %r', output)
 
 
-def merge_profiles(input_dir, output_file, input_extension, profdata_tool_path):
+def merge_profiles(input_dir,
+                   output_file,
+                   input_extension,
+                   profdata_tool_path,
+                   input_filename_pattern='.*'):
   """Merges the profiles produced by the shards using llvm-profdata.
 
   Args:
@@ -233,11 +240,15 @@
     input_extension (str): File extension to look for in the input_dir.
         e.g. '.profdata' or '.profraw'
     profdata_tool_path: The path to the llvm-profdata executable.
+    input_filename_pattern (str): The regex pattern of input filename. Should be
+        a valid regex pattern if present.
   Returns:
     The list of profiles that had to be excluded to get the merge to
     succeed and a list of profiles that had a counter overflow.
   """
-  profile_input_file_paths = _get_profile_paths(input_dir, input_extension)
+  profile_input_file_paths = _get_profile_paths(input_dir,
+                                                input_extension,
+                                                input_filename_pattern)
   invalid_profraw_files = []
   counter_overflows = []
   if input_extension == '.profraw':
diff --git a/testing/merge_scripts/code_coverage/merge_results.py b/testing/merge_scripts/code_coverage/merge_results.py
index 92f446ff..dd6b94c9 100755
--- a/testing/merge_scripts/code_coverage/merge_results.py
+++ b/testing/merge_scripts/code_coverage/merge_results.py
@@ -43,6 +43,7 @@
       '--profdata-dir', required=True, help='where to store the merged data')
   parser.add_argument(
       '--llvm-profdata', required=True, help='path to llvm-profdata executable')
+  parser.add_argument('--test-target-name', help='test target name')
   parser.add_argument(
       '--java-coverage-dir', help='directory for Java coverage data')
   parser.add_argument(
@@ -75,6 +76,10 @@
     coverage_merger.merge_java_exec_files(
         params.task_output_dir, output_path, params.jacococli_path)
 
+  # Name the output profdata file name as {test_target}.profdata or
+  # default.profdata.
+  output_prodata_filename = (params.test_target_name or 'default') + '.profdata'
+
   # NOTE: The coverage data merge script must make sure that the profraw files
   # are deleted from the task output directory after merging, otherwise, other
   # test results merge script such as layout tests will treat them as json test
@@ -82,7 +87,7 @@
   logging.info('Merging code coverage profraw data')
   invalid_profiles, counter_overflows = coverage_merger.merge_profiles(
       params.task_output_dir,
-      os.path.join(params.profdata_dir, 'default.profdata'), '.profraw',
+      os.path.join(params.profdata_dir, output_prodata_filename), '.profraw',
       params.llvm_profdata)
 
   # At the moment counter overflows overlap with invalid profiles, but this is
diff --git a/testing/merge_scripts/code_coverage/merge_results_test.py b/testing/merge_scripts/code_coverage/merge_results_test.py
index fe521ec..91e5e30 100755
--- a/testing/merge_scripts/code_coverage/merge_results_test.py
+++ b/testing/merge_scripts/code_coverage/merge_results_test.py
@@ -39,12 +39,13 @@
     })
     task_output_dir = 'some/task/output/dir'
     profdata_dir = '/some/different/path/to/profdata/default.profdata'
-    profdata_file = os.path.join(profdata_dir, 'default.profdata')
+    profdata_file = os.path.join(profdata_dir, 'base_unittests.profdata')
     args = [
         'script_name', '--output-json', 'output.json', '--build-properties',
         build_properties, '--summary-json', 'summary.json', '--task-output-dir',
         task_output_dir, '--profdata-dir', profdata_dir, '--llvm-profdata',
-        'llvm-profdata', 'a.json', 'b.json', 'c.json'
+        'llvm-profdata', 'a.json', 'b.json', 'c.json', '--test-target-name',
+        'base_unittests'
     ]
     with mock.patch.object(merger, 'merge_profiles') as mock_merge:
       mock_merge.return_value = None, None
@@ -53,7 +54,7 @@
         self.assertEqual(
             mock_merge.call_args,
             mock.call(task_output_dir, profdata_file, '.profraw',
-                      'llvm-profdata'))
+                      'llvm-profdata'), None)
 
   def test_merge_steps_parameters(self):
     """Test the build-level merge front-end."""
@@ -67,6 +68,8 @@
         output_file,
         '--llvm-profdata',
         'llvm-profdata',
+        '--profdata-filename-pattern',
+        '.*'
     ]
     with mock.patch.object(merger, 'merge_profiles') as mock_merge:
       mock_merge.return_value = None
@@ -74,7 +77,8 @@
         merge_steps.main()
         self.assertEqual(
             mock_merge.call_args,
-            mock.call(input_dir, output_file, '.profdata', 'llvm-profdata'))
+            mock.call(input_dir, output_file, '.profdata', 'llvm-profdata',
+                '.*'))
 
   @mock.patch.object(merger, '_validate_and_convert_profraws')
   def test_merge_profraw(self, mock_validate_and_convert_profraws):
@@ -163,6 +167,44 @@
     # The mock method should only apply when merging .profraw files.
     self.assertFalse(mock_validate_and_convert_profraws.called)
 
+  @mock.patch.object(merger, '_validate_and_convert_profraws')
+  def test_merge_profdata_pattern(self, mock_validate_and_convert_profraws):
+    mock_input_dir_walk = [
+        ('/b/some/path', ['base_unittests', 'url_unittests'], ['summary.json']),
+        ('/b/some/path/base_unittests', [], ['output.json',
+                                             'base_unittests.profdata']),
+        ('/b/some/path/url_unittests', [], ['output.json',
+                                            'url_unittests.profdata'],),
+        ('/b/some/path/ios_chrome_smoke_eg2tests',
+            [], ['output.json','ios_chrome_smoke_eg2tests.profdata'],),
+    ]
+    with mock.patch.object(os, 'walk') as mock_walk:
+      with mock.patch.object(os, 'remove'):
+        mock_walk.return_value = mock_input_dir_walk
+        with mock.patch.object(subprocess, 'check_output') as mock_exec_cmd:
+          input_profdata_filename_pattern = '.+_unittests\.profdata'
+          merger.merge_profiles('/b/some/path',
+                                'output/dir/default.profdata',
+                                '.profdata',
+                                'llvm-profdata',
+                                input_profdata_filename_pattern)
+          self.assertEqual(
+              mock.call(
+                  [
+                      'llvm-profdata',
+                      'merge',
+                      '-o',
+                      'output/dir/default.profdata',
+                      '-sparse=true',
+                      '/b/some/path/base_unittests/base_unittests.profdata',
+                      '/b/some/path/url_unittests/url_unittests.profdata',
+                  ],
+                  stderr=-2,
+              ), mock_exec_cmd.call_args)
+
+    # The mock method should only apply when merging .profraw files.
+    self.assertFalse(mock_validate_and_convert_profraws.called)
+
   def test_retry_profdata_merge_failures(self):
     mock_input_dir_walk = [
         ('/b/some/path', ['0', '1'], ['summary.json']),
@@ -228,6 +270,7 @@
     self.assertEqual(set(['44b643576cf39f10', '44b1234567890123']),
                      merger.get_shards_to_retry(bad_profiles))
 
+  @mock.patch('merge_lib._JAVA_PATH', 'java')
   def test_merge_java_exec_files(self):
     mock_input_dir_walk = [
         ('/b/some/path', ['0', '1', '2', '3'], ['summary.json']),
diff --git a/testing/merge_scripts/code_coverage/merge_steps.py b/testing/merge_scripts/code_coverage/merge_steps.py
index c0d5d4e..544edd2 100755
--- a/testing/merge_scripts/code_coverage/merge_steps.py
+++ b/testing/merge_scripts/code_coverage/merge_steps.py
@@ -18,6 +18,11 @@
       '--output-file', required=True, help='where to store the merged data')
   parser.add_argument(
       '--llvm-profdata', required=True, help='path to llvm-profdata executable')
+  parser.add_argument(
+      '--profdata-filename-pattern',
+      default='.*',
+      help='regex pattern of profdata filename to merge for current test type. '
+          'If not present, all profdata files will be merged.')
   return parser
 
 
@@ -26,7 +31,7 @@
   parser = _merge_steps_argument_parser(description=desc)
   params = parser.parse_args()
   merger.merge_profiles(params.input_dir, params.output_file, '.profdata',
-                        params.llvm_profdata)
+                        params.llvm_profdata, params.profdata_filename_pattern)
 
 
 if __name__ == '__main__':
diff --git a/third_party/blink/public/mojom/BUILD.gn b/third_party/blink/public/mojom/BUILD.gn
index 667c4bd..43379f4 100644
--- a/third_party/blink/public/mojom/BUILD.gn
+++ b/third_party/blink/public/mojom/BUILD.gn
@@ -265,7 +265,6 @@
     "payments/payment_handler_host.mojom",
     "payments/payment_request.mojom",
     "webauthn/authenticator.mojom",
-    "webauthn/internal_authenticator.mojom",
     "webshare/webshare.mojom",
   ]
 
diff --git a/third_party/blink/public/mojom/webauthn/internal_authenticator.mojom b/third_party/blink/public/mojom/webauthn/internal_authenticator.mojom
deleted file mode 100644
index a1c3aee..0000000
--- a/third_party/blink/public/mojom/webauthn/internal_authenticator.mojom
+++ /dev/null
@@ -1,31 +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.
-
-module blink.mojom;
-
-import "third_party/blink/public/mojom/webauthn/authenticator.mojom";
-
-
-// Interface similar to blink::mojom::Authenticator meant only for internal
-// components in Chrome to use in order to direct authenticators to create or
-// use a public key credential. Unlike Authenticator, the caller will be
-// allowed to set its own effective origin.
-interface InternalAuthenticator {
-  // Gets the credential info for a new public key credential created by an
-  // authenticator for the given |PublicKeyCredentialCreationOptions|
-  // [MakeCredentialAuthenticatorResponse] will be set if and only if status == SUCCESS.
-  MakeCredential(PublicKeyCredentialCreationOptions options)
-      => (AuthenticatorStatus status, MakeCredentialAuthenticatorResponse? credential);
-
-  // Uses an existing credential to produce an assertion for the given
-  // |PublicKeyCredentialRequestOptions|.
-  // |GetAssertionResponse| will be set if and only if status == SUCCESS.
-  GetAssertion(PublicKeyCredentialRequestOptions options)
-      => (AuthenticatorStatus status, GetAssertionAuthenticatorResponse? credential);
-
-  // Returns true if the user platform provides an authenticator. Relying
-  // Parties use this method to determine whether they can create a new
-  // credential using a user-verifying platform authenticator.
-  IsUserVerifyingPlatformAuthenticatorAvailable() => (bool available);
-};
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.cc b/third_party/blink/renderer/core/css/parser/css_parser.cc
index 4cf1abc..37d310b 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.cc
+++ b/third_party/blink/renderer/core/css/parser/css_parser.cc
@@ -11,6 +11,7 @@
 #include "third_party/blink/renderer/core/css/parser/css_parser_fast_paths.h"
 #include "third_party/blink/renderer/core/css/parser/css_parser_impl.h"
 #include "third_party/blink/renderer/core/css/parser/css_property_parser.h"
+#include "third_party/blink/renderer/core/css/parser/css_property_parser_helpers.h"
 #include "third_party/blink/renderer/core/css/parser/css_selector_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_supports_parser.h"
 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
@@ -275,4 +276,16 @@
   return value;
 }
 
+CSSPrimitiveValue* CSSParser::ParseLengthPercentage(
+    const String& string,
+    const CSSParserContext* context) {
+  if (string.IsEmpty() || !context)
+    return nullptr;
+  CSSTokenizer tokenizer(string);
+  const auto tokens = tokenizer.TokenizeToEOF();
+  CSSParserTokenRange range(tokens);
+  return css_property_parser_helpers::ConsumeLengthOrPercent(range, *context,
+                                                             kValueRangeAll);
+}
+
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/css/parser/css_parser.h b/third_party/blink/renderer/core/css/parser/css_parser.h
index 6de5b9b..df51b7f0 100644
--- a/third_party/blink/renderer/core/css/parser/css_parser.h
+++ b/third_party/blink/renderer/core/css/parser/css_parser.h
@@ -23,6 +23,7 @@
 class StyleRuleKeyframe;
 class StyleSheetContents;
 class CSSValue;
+class CSSPrimitiveValue;
 enum class ParseSheetResult;
 enum class SecureContextMode;
 
@@ -112,6 +113,9 @@
                                                const String&,
                                                CSSParserObserver&);
 
+  static CSSPrimitiveValue* ParseLengthPercentage(const String&,
+                                                  const CSSParserContext*);
+
  private:
   static MutableCSSPropertyValueSet::SetResult ParseValue(
       MutableCSSPropertyValueSet*,
diff --git a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h
index 1db6ee2..9a2c289 100644
--- a/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h
+++ b/third_party/blink/renderer/core/html/canvas/canvas_async_blob_creator.h
@@ -50,8 +50,7 @@
   enum ToBlobFunctionType {
     kHTMLCanvasToBlobCallback,
     kHTMLCanvasConvertToBlobPromise,
-    kOffscreenCanvasConvertToBlobPromise,
-    kNumberOfToBlobFunctionTypes
+    kOffscreenCanvasConvertToBlobPromise
   };
 
   void ScheduleAsyncBlobCreation(const double& quality);
diff --git a/third_party/blink/renderer/core/mathml/mathml_element.cc b/third_party/blink/renderer/core/mathml/mathml_element.cc
index 43705b9..9e1a425 100644
--- a/third_party/blink/renderer/core/mathml/mathml_element.cc
+++ b/third_party/blink/renderer/core/mathml/mathml_element.cc
@@ -111,14 +111,12 @@
     const QualifiedName& attr_name) {
   if (!FastHasAttribute(attr_name))
     return base::nullopt;
-  auto string = FastGetAttribute(attr_name);
-  const CSSValue* parsed = CSSParser::ParseSingleValue(
-      CSSPropertyID::kHeight, string,
-      StrictCSSParserContext(GetDocument().GetSecureContextMode()));
-  const auto* new_value = DynamicTo<CSSPrimitiveValue>(parsed);
-  if (!new_value || !new_value->IsLength())
+  auto value = FastGetAttribute(attr_name);
+  const CSSPrimitiveValue* parsed_value = CSSParser::ParseLengthPercentage(
+      value, StrictCSSParserContext(GetDocument().GetSecureContextMode()));
+  if (!parsed_value || parsed_value->IsCalculated())
     return base::nullopt;
-  return new_value->ConvertToLength(conversion_data);
+  return parsed_value->ConvertToLength(conversion_data);
 }
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
index ce7d9b18..3570a6d 100644
--- a/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
+++ b/third_party/blink/renderer/core/paint/compositing/compositing_reason_finder.cc
@@ -10,6 +10,7 @@
 #include "third_party/blink/renderer/core/frame/local_frame_view.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
 #include "third_party/blink/renderer/core/fullscreen/fullscreen.h"
+#include "third_party/blink/renderer/core/html/html_body_element.h"
 #include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
 #include "third_party/blink/renderer/core/layout/layout_video.h"
 #include "third_party/blink/renderer/core/layout/layout_view.h"
@@ -103,6 +104,26 @@
   return reasons;
 }
 
+static bool ShouldPreferCompositingForLayoutView(
+    const LayoutView& layout_view) {
+  auto has_direct_compositing_reasons = [](const LayoutObject* object) -> bool {
+    return object && CompositingReasonFinder::DirectReasonsForPaintProperties(
+                         *object) != CompositingReason::kNone;
+  };
+  if (has_direct_compositing_reasons(
+          layout_view.GetFrame()->OwnerLayoutObject()))
+    return true;
+  if (auto* document_element = layout_view.GetDocument().documentElement()) {
+    if (has_direct_compositing_reasons(document_element->GetLayoutObject()))
+      return true;
+  }
+  if (auto* body = layout_view.GetDocument().FirstBodyElement()) {
+    if (has_direct_compositing_reasons(body->GetLayoutObject()))
+      return true;
+  }
+  return false;
+}
+
 CompositingReasons CompositingReasonFinder::DirectReasonsForPaintProperties(
     const LayoutObject& object) {
   // TODO(wangxianzhu): Don't depend on PaintLayer for CompositeAfterPaint.
@@ -143,15 +164,9 @@
           // a direct compositing reason, it's very likely that the object will
           // be composited, and it also indicates preference of compositing,
           // so we prefer composited scrolling here.
-          style.BackfaceVisibility() == EBackfaceVisibility::kHidden;
-
-      if (!force_prefer_compositing_to_lcd_text && object.IsLayoutView()) {
-        if (auto* owner_object = object.GetFrame()->OwnerLayoutObject()) {
-          force_prefer_compositing_to_lcd_text =
-              DirectReasonsForPaintProperties(*owner_object) !=
-              CompositingReason::kNone;
-        }
-      }
+          style.BackfaceVisibility() == EBackfaceVisibility::kHidden ||
+          (object.IsLayoutView() &&
+           ShouldPreferCompositingForLayoutView(To<LayoutView>(object)));
 
       if (scrollable_area->ComputeNeedsCompositedScrolling(
               force_prefer_compositing_to_lcd_text)) {
diff --git a/third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h b/third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h
index a6b8547..5fa03cb 100644
--- a/third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h
+++ b/third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h
@@ -36,7 +36,7 @@
   template <typename U, std::enable_if_t<std::is_base_of<T, U>::value, int> = 0>
   NotShared(const NotShared<U>& other) : typed_array_(other.Get()) {}
 
-  explicit NotShared(nullptr_t) {}
+  explicit NotShared(std::nullptr_t) {}
   explicit NotShared(T* typed_array) : typed_array_(typed_array) {
     DCHECK(!typed_array || !typed_array->IsShared());
   }
@@ -95,7 +95,7 @@
   template <typename U, std::enable_if_t<std::is_base_of<T, U>::value, int> = 0>
   MaybeShared(const MaybeShared<U>& other) : typed_array_(other.Get()) {}
 
-  explicit MaybeShared(nullptr_t) {}
+  explicit MaybeShared(std::nullptr_t) {}
   // [AllowShared] array buffer view may be a view of non-shared array buffer,
   // so we don't check if the buffer is SharedArrayBuffer or not.
   // https://heycam.github.io/webidl/#AllowShared
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 49fb50f..4500a9e8e 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
@@ -6,6 +6,7 @@
 
 #include "third_party/blink/public/platform/platform.h"
 #include "third_party/blink/public/strings/grit/blink_strings.h"
+#include "third_party/blink/renderer/core/dom/document.h"
 #include "third_party/blink/renderer/core/dom/events/event.h"
 #include "third_party/blink/renderer/core/frame/local_frame_client.h"
 #include "third_party/blink/renderer/core/frame/settings.h"
@@ -76,7 +77,7 @@
     ResourceRequest request(url);
     request.SetSuggestedFilename(MediaElement().title());
     request.SetRequestContext(mojom::RequestContextType::DOWNLOAD);
-    request.SetRequestorOrigin(SecurityOrigin::Create(GetDocument().Url()));
+    request.SetRequestorOrigin(GetDocument().GetSecurityOrigin());
     GetDocument().GetFrame()->DownloadURL(
         request, network::mojom::blink::RedirectMode::kError);
   }
diff --git a/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc b/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
index 62a9170c..819ca91 100644
--- a/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
+++ b/third_party/blink/renderer/platform/audio/audio_delay_dsp_kernel.cc
@@ -61,7 +61,8 @@
   // Compute the length of the buffer needed to handle a max delay of
   // |maxDelayTime|. One is added to handle the case where the actual delay
   // equals the maximum delay.
-  return 1 + audio_utilities::TimeToSampleFrame(max_delay_time, sample_rate);
+  return 1 + audio_utilities::TimeToSampleFrame(max_delay_time, sample_rate,
+                                                audio_utilities::kRoundUp);
 }
 
 bool AudioDelayDSPKernel::HasSampleAccurateValues() {
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.cc b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
index 2e992dfac..a02a45e 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.cc
@@ -701,6 +701,15 @@
                              NumberParsingOptions::kStrict, ok);
 }
 
+uint64_t StringImpl::HexToUInt64Strict(bool* ok) {
+  if (Is8Bit()) {
+    return HexCharactersToUInt64(Characters8(), length_,
+                                 NumberParsingOptions::kStrict, ok);
+  }
+  return HexCharactersToUInt64(Characters16(), length_,
+                               NumberParsingOptions::kStrict, ok);
+}
+
 int64_t StringImpl::ToInt64(NumberParsingOptions options, bool* ok) const {
   if (Is8Bit())
     return CharactersToInt64(Characters8(), length_, options, ok);
diff --git a/third_party/blink/renderer/platform/wtf/text/string_impl.h b/third_party/blink/renderer/platform/wtf/text/string_impl.h
index 4228268..2083ac0 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_impl.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_impl.h
@@ -345,6 +345,7 @@
   uint64_t ToUInt64(NumberParsingOptions, bool* ok) const;
 
   wtf_size_t HexToUIntStrict(bool* ok);
+  uint64_t HexToUInt64Strict(bool* ok);
 
   // FIXME: Like NumberParsingOptions::kStrict, these give false for "ok" when
   // there is trailing garbage.  Like NumberParsingOptions::kLoose, these return
diff --git a/third_party/blink/renderer/platform/wtf/text/string_to_number.cc b/third_party/blink/renderer/platform/wtf/text/string_to_number.cc
index d00bd260..d915df41 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_to_number.cc
+++ b/third_party/blink/renderer/platform/wtf/text/string_to_number.cc
@@ -175,6 +175,20 @@
   return ToIntegralType<unsigned, UChar, 16>(data, length, options, ok);
 }
 
+uint64_t HexCharactersToUInt64(const LChar* data,
+                               size_t length,
+                               NumberParsingOptions options,
+                               bool* ok) {
+  return ToIntegralType<uint64_t, LChar, 16>(data, length, options, ok);
+}
+
+uint64_t HexCharactersToUInt64(const UChar* data,
+                               size_t length,
+                               NumberParsingOptions options,
+                               bool* ok) {
+  return ToIntegralType<uint64_t, UChar, 16>(data, length, options, ok);
+}
+
 int CharactersToInt(const LChar* data,
                     size_t length,
                     NumberParsingOptions options,
diff --git a/third_party/blink/renderer/platform/wtf/text/string_to_number.h b/third_party/blink/renderer/platform/wtf/text/string_to_number.h
index ae9fded..f6e7804 100644
--- a/third_party/blink/renderer/platform/wtf/text/string_to_number.h
+++ b/third_party/blink/renderer/platform/wtf/text/string_to_number.h
@@ -40,6 +40,14 @@
                                         size_t,
                                         NumberParsingOptions,
                                         bool* ok);
+WTF_EXPORT uint64_t HexCharactersToUInt64(const UChar*,
+                                          size_t,
+                                          NumberParsingOptions,
+                                          bool* ok);
+WTF_EXPORT uint64_t HexCharactersToUInt64(const LChar*,
+                                          size_t,
+                                          NumberParsingOptions,
+                                          bool* ok);
 WTF_EXPORT unsigned CharactersToUInt(const LChar*,
                                      size_t,
                                      NumberParsingOptions,
diff --git a/third_party/blink/renderer/platform/wtf/text/wtf_string.cc b/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
index 5d7213c..c32a41c 100644
--- a/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
+++ b/third_party/blink/renderer/platform/wtf/text/wtf_string.cc
@@ -295,6 +295,15 @@
   return impl_->HexToUIntStrict(ok);
 }
 
+uint64_t String::HexToUInt64Strict(bool* ok) const {
+  if (!impl_) {
+    if (ok)
+      *ok = false;
+    return 0;
+  }
+  return impl_->HexToUInt64Strict(ok);
+}
+
 int64_t String::ToInt64Strict(bool* ok) const {
   if (!impl_) {
     if (ok)
diff --git a/third_party/blink/renderer/platform/wtf/text/wtf_string.h b/third_party/blink/renderer/platform/wtf/text/wtf_string.h
index df24384..a50371b 100644
--- a/third_party/blink/renderer/platform/wtf/text/wtf_string.h
+++ b/third_party/blink/renderer/platform/wtf/text/wtf_string.h
@@ -422,6 +422,7 @@
   int ToIntStrict(bool* ok = nullptr) const;
   unsigned ToUIntStrict(bool* ok = nullptr) const;
   unsigned HexToUIntStrict(bool* ok) const;
+  uint64_t HexToUInt64Strict(bool* ok) const;
   int64_t ToInt64Strict(bool* ok = nullptr) const;
   uint64_t ToUInt64Strict(bool* ok = nullptr) const;
 
diff --git a/third_party/blink/web_tests/FlagExpectations/composite-after-paint b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
index 9051e6c3..ccaa477 100644
--- a/third_party/blink/web_tests/FlagExpectations/composite-after-paint
+++ b/third_party/blink/web_tests/FlagExpectations/composite-after-paint
@@ -80,8 +80,6 @@
 
 # We paint the iframe's content background in the scrolling layer, causing invalidation on scroll.
 paint/invalidation/scroll/iframe-scroll-repaint.html [ Failure ]
-# will-transform on descendant doesn't trigger compositing of iframe.
-paint/invalidation/scroll/composited-iframe-scroll-repaint.html [ Failure ]
 
 # Extra layers for non-fast scrolling areas.
 compositing/overflow/textarea-scroll-touch.html [ Failure ]
diff --git a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
index c0abca6..19dfbdd8 100644
--- a/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
+++ b/third_party/blink/web_tests/FlagExpectations/layout-ng-fragment-item
@@ -94,7 +94,8 @@
 crbug.com/852645 external/wpt/fullscreen/api/element-request-fullscreen-two-iframes-manual.html [ Timeout ]
 crbug.com/982194 external/wpt/html/dom/elements/requirements-relating-to-bidirctional-algorithm-formatting-characters/dir-isolation-005c.html [ Failure ]
 crbug.com/982194 external/wpt/html/rendering/non-replaced-elements/the-fieldset-and-legend-elements/fieldset-vertical.html [ Failure ]
-crbug.com/736415 external/wpt/html/user-activation/activation-api-iframe.tentative.html [ Failure ]
+crbug.com/736415 external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html [ Failure ]
+crbug.com/736415 external/wpt/html/user-activation/navigation-state-reset-sameorigin.tentative.html [ Failure ]
 crbug.com/626703 external/wpt/infrastructure/testdriver/actions/eventOrder.html [ Timeout ]
 crbug.com/982194 external/wpt/intersection-observe/v2/scaled-target.html [ Failure ]
 crbug.com/982194 external/wpt/pointerevents/extension/pointerevent_touch-action-pan-down-css_touch.html [ Timeout ]
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index dad01ff..ab7503ac 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -48,6 +48,9 @@
 crbug.com/1048761 external/wpt/infrastructure/server/http2-context.sub.h2.any.worker.html [ Skip ]
 crbug.com/1048761 external/wpt/infrastructure/server/wpt-server-http.sub.html [ Skip ]
 
+# WPT Test harness doesn't deal with finding an about:blank ref test
+crbug.com/1066130 external/wpt/infrastructure/assumptions/blank.html [ Failure ]
+
 # Favicon is not supported by run_web_tests.
 external/wpt/fetch/metadata/favicon.https.sub.html [ Skip ]
 virtual/omt-worker-fetch/external/wpt/fetch/metadata/favicon.https.sub.html [ Skip ]
@@ -1627,7 +1630,6 @@
 crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-006.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction-009.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/direction/direction.html [ Failure ]
-crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-001.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-002.html [ Failure ]
 crbug.com/6606 external/wpt/mathml/presentation-markup/fractions/frac-parameters-gap-003.html [ Failure ]
@@ -2023,7 +2025,6 @@
 crbug.com/613672 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-zoom-in-slow-desktop.html [ Skip ]
 crbug.com/613672 [ Mac ] virtual/threaded/synthetic_gestures/synthetic-pinch-zoom-gesture-touchscreen-desktop.html [ Skip ]
 
-crbug.com/652536 fast/events/mouse-cursor-change-after-image-load.html [ Failure Pass Crash ]
 crbug.com/520188 [ Win ] http/tests/local/fileapi/file-last-modified-after-delete.html [ Failure Pass ]
 crbug.com/520611 [ Debug ] fast/filesystem/workers/file-writer-events-shared-worker.html [ Failure Pass ]
 crbug.com/520194 http/tests/xmlhttprequest/timeout/xmlhttprequest-timeout-worker-overridesexpires.html [ Failure Pass ]
@@ -5220,7 +5221,8 @@
 crbug.com/855816 [ Win ] virtual/stable/http/tests/navigation/rename-subframe-goback.html [ Pass Timeout ]
 
 # User Activation
-crbug.com/736415 external/wpt/html/user-activation/activation-api-iframe.tentative.html [ Failure ]
+crbug.com/736415 external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html [ Failure ]
+crbug.com/736415 external/wpt/html/user-activation/navigation-state-reset-sameorigin.tentative.html [ Failure ]
 
 # Sheriff 2018-07-05
 crbug.com/860731 fast/scroll-snap/animate-fling-to-snap-points.html [ Failure Pass ]
@@ -5322,10 +5324,6 @@
 # Sheriff 2018-10-15
 crbug.com/895257 [ Mac ] external/wpt/css/css-fonts/variations/at-font-face-font-matching.html [ Failure Pass ]
 
-# run_web_tests.py crashes content_shell when <link href="about:blank"> is present
-crbug.com/895777 external/wpt/css/css-transforms/text-perspective-001.html [ Pass Crash ]
-crbug.com/895777 external/wpt/infrastructure/assumptions/blank.html [ Failure Crash ]
-
 #Sheriff 2018-10-23
 crbug.com/898378 [ Mac10.13 ] fast/scroll-behavior/smooth-scroll/keyboard-scroll.html [ Timeout ]
 
@@ -5603,9 +5601,6 @@
 crbug.com/931533 media/video-played-collapse.html [ Pass Failure ]
 crbug.com/931533 virtual/audio-service/media/video-played-collapse.html [ Pass Failure ]
 
-# Test was blocking WPT importer
-crbug.com/941471 external/wpt/css/css-transforms/transform-flattening-001.html [ Pass Failure Crash ]
-
 # Sheriff 2019-03-14
 crbug.com/806357 virtual/threaded/fast/events/pointerevents/pinch/pointerevent_touch-action-pinch_zoom_touch.html [ Pass Failure Crash Timeout ]
 crbug.com/941931 [ Linux ] http/tests/security/contentSecurityPolicy/1.1/plugintypes-affects-cross-site-child-disallowed.html [ Pass Failure ]
diff --git a/third_party/blink/web_tests/WPTOverrideExpectations b/third_party/blink/web_tests/WPTOverrideExpectations
index 985f340..f37c2557 100644
--- a/third_party/blink/web_tests/WPTOverrideExpectations
+++ b/third_party/blink/web_tests/WPTOverrideExpectations
@@ -576,10 +576,6 @@
 external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-top-left.html [ Pass Failure ]
 external/wpt/html/browsers/the-window-object/apis-for-creating-and-navigating-browsing-contexts-by-name/open-features-tokenization-width-height.html [ Pass Failure ]
 external/wpt/html/browsers/windows/noreferrer-window-name.html [ Timeout ] # wpt_subtest_failure
-external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html [ Timeout ]
-external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html [ Timeout ] # wpt_subtest_failure
-external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-service-worker.https.html [ Timeout ] # wpt_subtest_failure
-external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html [ Timeout ] # wpt_subtest_failure
 external/wpt/html/cross-origin-embedder-policy/coep-frame-javascript.https.html [ Pass Timeout ] # wpt_subtest_failure
 external/wpt/html/cross-origin-embedder-policy/javascript.https.html [ Pass Timeout ] # wpt_subtest_failure
 external/wpt/html/cross-origin-embedder-policy/reporting.https.html [ Pass Failure ] # wpt_subtest_failure
@@ -677,8 +673,8 @@
 external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html [ Failure Pass ] # wpt_subtest_failure
 external/wpt/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html [ Failure Pass ] # wpt_subtest_failure
 external/wpt/html/semantics/scripting-1/the-script-element/module/module-in-xhtml.xhtml [ Failure Pass ] # wpt_subtest_failure
-external/wpt/html/user-activation/activation-api-iframe.tenative.html [ Failure Pass ] # wpt_subtest_failure
-external/wpt/html/user-activation/activation-api-iframe.tentative.html [ Pass Failure ] # wpt_subtest_failure
+external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html [ Failure ]
+external/wpt/html/user-activation/navigation-state-reset-sameorigin.tentative.html [ Failure ]
 external/wpt/html/user-activation/activation-transfer-cross-origin-with-click-two-child-frames.sub.tentative.html [ Failure Pass ] # wpt_subtest_failure
 external/wpt/html/user-activation/activation-transfer-cross-origin-with-click.sub.tentative.html [ Failure Pass ] # wpt_subtest_failure
 external/wpt/html/user-activation/activation-transfer-with-click.tentative.html [ Failure Pass ] # wpt_subtest_failure
diff --git a/third_party/blink/web_tests/custom-elements/spec/construct.html b/third_party/blink/web_tests/custom-elements/spec/construct.html
index 658efc4..f158345 100644
--- a/third_party/blink/web_tests/custom-elements/spec/construct.html
+++ b/third_party/blink/web_tests/custom-elements/spec/construct.html
@@ -14,13 +14,13 @@
 });
 
 test_with_window((w) => {
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.HTMLElement();
   }, 'calling the HTMLElement constructor should throw a TypeError');
 }, 'HTMLElement constructor, invoke');
 
 test_with_window((w) => {
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     new w.HTMLElement();
   }, 'invoking the HTMLElement constructor with a construct call should ' +
      'throw a TypeError');
@@ -29,7 +29,7 @@
 test_with_window((w) => {
   class X extends w.HTMLElement {}
   w.customElements.define('a-a', X);
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(TypeError, () => {
     X();
   }, 'calling a custom element constructor should throw a TypeError');
 }, 'Custom element constructor, invoke');
@@ -69,12 +69,12 @@
   class C extends w.HTMLElement { }
   class D extends C {}
   w.customElements.define('a-a', C); // Note: Not D.
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     new D();
   }, 'constructing a custom element with a new.target with no registry ' +
      'entry should throw a TypeError');
 
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(TypeError, () => {
     Reflect.construct(C, [], 42);
   }, 'constructing a custom element with a non-object new.target should ' +
      'throw a TypeError');
@@ -82,7 +82,7 @@
 
 test_with_window((w) => {
   w.customElements.define('a-a', w.HTMLButtonElement);
-  assert_reports(w, TypeError.prototype, () => {
+  assert_reports(w, w.TypeError, () => {
     w.document.createElement('a-a');
   }, 'If NewTarget is equal to active function object, TypeError should be ' +
      'reported');
@@ -90,7 +90,7 @@
 
 test_with_window((w) => {
   w.customElements.define('a-a', class extends w.HTMLButtonElement {} );
-  assert_reports(w, TypeError.prototype, () => {
+  assert_reports(w, w.TypeError, () => {
     w.document.createElement('a-a');
   }, 'If NewTarget is equal to active function object, TypeError should be ' +
      'reported');
diff --git a/third_party/blink/web_tests/custom-elements/spec/create-element-defined-synchronous.html b/third_party/blink/web_tests/custom-elements/spec/create-element-defined-synchronous.html
index fe90db7c..6b622f08 100644
--- a/third_party/blink/web_tests/custom-elements/spec/create-element-defined-synchronous.html
+++ b/third_party/blink/web_tests/custom-elements/spec/create-element-defined-synchronous.html
@@ -11,9 +11,6 @@
   allow_uncaught_exception: true,
 });
 
-const expectTypeError = TypeError.prototype;
-const expectNotSupportedError = 'NOT_SUPPORTED_ERR';
-
 // https://dom.spec.whatwg.org/#concept-create-element
 // 5. If definition is non-null and the definition represents a customized built-in element:
 // 5.3. If the synchronous custom elements flag is set:
@@ -87,7 +84,7 @@
   }, `${description}6.1.2. Errors in Construct(C) should be reported`);
 
   test_with_window((w) => {
-    assert_reports(w, expectTypeError, () => {
+    assert_reports(w, w.TypeError, () => {
       define_and_create_element(w, class {
         constructor() {}
       });
@@ -95,7 +92,7 @@
   }, `${description}6.1.3. If result does not implement the HTMLElement interface, throw a TypeError`);
 
   test_with_window((w) => {
-    assert_reports(w, expectNotSupportedError, () => {
+    assert_reports(w, 'NotSupportedError', () => {
       define_and_create_element(w, class extends w.HTMLElement {
         constructor() { super(); this.setAttribute('a', 'a'); }
       });
@@ -103,7 +100,7 @@
   }, `${description}6.1.4. If result\'s attribute list is not empty, then throw a NotSupportedError`);
 
   test_with_window((w) => {
-    assert_reports(w, expectNotSupportedError, () => {
+    assert_reports(w, 'NotSupportedError', () => {
       define_and_create_element(w, class extends w.HTMLElement {
         constructor() { super(); this.appendChild(w.document.createElement('a')); }
       });
@@ -111,7 +108,7 @@
   }, `${description}6.1.5. If result has children, then throw a NotSupportedError`);
 
   test_with_window((w) => {
-    assert_reports(w, expectNotSupportedError, () => {
+    assert_reports(w, 'NotSupportedError', () => {
       define_and_create_element(w, class extends w.HTMLElement {
         constructor() { super(); this.append('a'); }
       });
@@ -119,7 +116,7 @@
   }, `${description}6.1.5. If result has children, then throw a NotSupportedError`);
 
   test_with_window((w) => {
-    assert_reports(w, expectNotSupportedError, () => {
+    assert_reports(w, 'NotSupportedError', () => {
       define_and_create_element(w, class extends w.HTMLElement {
         constructor() { super(); w.document.createElement('div').appendChild(this); }
       });
@@ -127,7 +124,7 @@
   }, `${description}6.1.6. If result\'s parent is not null, then throw a NotSupportedError`);
 
   test_with_window((w) => {
-    assert_reports(w, expectNotSupportedError, () => {
+    assert_reports(w, 'NotSupportedError', () => {
       define_and_create_element(w, class extends w.HTMLElement {
         constructor() { super(); return w.document.implementation.createHTMLDocument().createElement('div'); }
       });
@@ -137,7 +134,7 @@
   // See the note in step 6.1.8. In future, it may be possible to throw
   // NotSupportedError for some elements.
   test_with_window((w) => {
-    assert_reports(w, expectTypeError, () => {
+    assert_reports(w, w.TypeError, () => {
       define_and_create_element(w, class extends w.HTMLElement {
         constructor() { super(); return w.document.createElementNS('http://www.w3.org/2000/svg', 'g'); }
       });
@@ -145,7 +142,7 @@
   }, `${description}6.1.8. If result\'s namespace is not the HTML namespace, then throw (a NotSupportedError, currently TypeError)`);
 
   test_with_window((w) => {
-    assert_reports(w, expectNotSupportedError, () => {
+    assert_reports(w, 'NotSupportedError', () => {
       define_and_create_element(w, class extends w.HTMLElement {
         constructor() { super(); return document.createElement('div'); }
       });
diff --git a/third_party/blink/web_tests/custom-elements/spec/define-element.html b/third_party/blink/web_tests/custom-elements/spec/define-element.html
index 034d12339..1475f06 100644
--- a/third_party/blink/web_tests/custom-elements/spec/define-element.html
+++ b/third_party/blink/web_tests/custom-elements/spec/define-element.html
@@ -13,13 +13,13 @@
 'use strict';
 
 test_with_window((w) => {
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.customElements.define('a-a', 42);
   }, 'defining a number "constructor" should throw a TypeError');
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.customElements.define('a-a', () => {});
   }, 'defining an arrow function "constructor" should throw a TypeError');
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.customElements.define('a-a', { m() {} }.m);
   }, 'defining a concise method "constructor" should throw a TypeError');
 }, 'A "constructor" that is not a constructor');
@@ -107,8 +107,8 @@
   .then(([w1, w2]) => {
     class X extends w2.HTMLElement { };
     w1.customElements.define('x-x', X);
-    assert_throws(
-      TypeError.prototype, () => new X(),
+    assert_throws_js(
+      w2.TypeError, () => new X(),
       'the current global object (w2) should not find the definition in w1');
   });
 }, 'HTMLElement constructor looks up definitions in the current global');
@@ -180,7 +180,7 @@
    '<first-name></first-name>');
 
 test_with_window((w) => {
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     let not_a_constructor = () => {};
     let invalid_name = 'annotation-xml';
     w.customElements.define(invalid_name, not_a_constructor);
@@ -265,10 +265,11 @@
 test_with_window((w) => {
   class Y extends w.HTMLElement {}
   let X = (function () {}).bind({});
+  let exception = { name: 42 };
   Object.defineProperty(X, 'prototype', {
-    get() { throw { name: 42 }; }
+    get() { throw exception; }
   });
-  assert_throws({ name: 42 }, () => {
+  assert_throws_exactly(exception, () => {
     w.customElements.define('a-a', X);
   }, 'should rethrow constructor exception');
   w.customElements.define('a-a', Y);
@@ -278,10 +279,11 @@
 // 14.1 Let prototype be Get(constructor, "prototype"). Rethrow any exceptions.
 test_with_window((w) => {
   let X = (function () {}).bind({});
+  let exception = {name: 'prototype throws' };
   Object.defineProperty(X, 'prototype', {
-    get() { throw { name: 'prototype throws' }; }
+    get() { throw exception; }
   });
-  assert_throws({ name: 'prototype throws' }, () => {
+  assert_throws_exactly(exception, () => {
     w.customElements.define('a-a', X);
   }, 'Exception from Get(constructor, prototype) should be rethrown');
 }, 'Rethrow any exceptions thrown while getting prototype');
@@ -290,7 +292,7 @@
 test_with_window((w) => {
   function F() {}
   F.prototype = 42;
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.customElements.define('a-a', F);
   }, 'defining an element with a constructor with a prototype that is not an ' +
      'object should throw a TypeError');
@@ -305,10 +307,11 @@
 function F_for_callbacks_in_reverse() {};
 callbacks_in_reverse.forEach((callback) => {
   test_with_window((w) => {
+    let exception = { name: callback };
     Object.defineProperty(F_for_callbacks_in_reverse.prototype, callback, {
-      get() { throw { name: callback }; }
+      get() { throw exception; }
     });
-    assert_throws({ name: callback }, () => {
+    assert_throws_exactly(exception, () => {
       w.customElements.define('a-a', F_for_callbacks_in_reverse);
     }, 'Exception from Get(prototype, callback) should be rethrown');
   }, 'Rethrow any exceptions thrown while retrieving ' + callback);
@@ -326,7 +329,7 @@
       Object.defineProperty(F.prototype, callback, {
         get() { return {}; }
       });
-      assert_throws(TypeError.prototype, () => {
+      assert_throws_js(w.TypeError, () => {
         w.customElements.define('a-a', F);
       }, 'defining an element with a constructor with a callback that is ' +
        'not undefined and not callable should throw a TypeError');
@@ -336,12 +339,13 @@
 // 14.9.2 Let observedAttributesIterable be Get(constructor, "observedAttributes").
 //        Rethrow any exceptions.
 test_with_window((w) => {
+  let exception = { name: 'observedAttributes throws' };
   class X extends w.HTMLElement{
     constructor() { super(); }
     attributeChangedCallback() {}
-    static get observedAttributes() { throw { name: 'observedAttributes throws' }; }
+    static get observedAttributes() { throw exception; }
   }
-  assert_throws({ name: 'observedAttributes throws' }, () => {
+  assert_throws_exactly(exception, () => {
     w.customElements.define('a-a', X);
   }, 'Exception from Get(constructor, observedAttributes) should be rethrown');
 }, 'Rethrow any exceptions thrown while getting observedAttributes');
@@ -376,7 +380,7 @@
   let constructor = function () {};
   constructor.prototype.attributeChangedCallback = function () { };
   constructor.observedAttributes = {[Symbol.iterator]: 1};
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.customElements.define('a-a', constructor);
   }, 'converting value that is not an object should throw TypeError');
 }, 'Converting non-object observedAttributes to sequence<DOMString>');
@@ -387,7 +391,7 @@
     attributeChangedCallback() {}
     static get observedAttributes() { return new RegExp(); }
   }
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.customElements.define('a-a', X);
   }, 'converting RegExp should throw TypeError');
 }, 'Converting regular expression observedAttributes to sequence<DOMString>');
@@ -396,7 +400,7 @@
   let constructor = function () {};
   constructor.prototype.attributeChangedCallback = function () { };
   constructor.observedAttributes = {};
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(w.TypeError, () => {
     w.customElements.define('a-a', constructor);
   }, 'If iterator method is undefined, it should throw TypeError');
 }, 'Converting observedAttributes without iterator method to sequence<DOMString>');
@@ -425,7 +429,7 @@
       return attributes;
     }
   }
-  assert_throws(TypeError.prototype, () => {
+  assert_throws_js(TypeError, () => {
     w.customElements.define('x-x', X);
   });
 }, 'Throwing an exception in observedAttributes');
diff --git a/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js b/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js
index 7de434d1..5407900 100644
--- a/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js
+++ b/third_party/blink/web_tests/custom-elements/spec/resources/custom-elements-helpers.js
@@ -21,9 +21,12 @@
   }, name);
 }
 
+// TODO(1066131): After https://github.com/web-platform-tests/wpt/pull/21876 is
+// rolled into Chromium, this function can be replaced with:
+//   assert_throws_dom(code, global_context.DOMException, func, description);
 function assert_throws_dom_exception(global_context, code, func, description) {
   let exception;
-  assert_throws(code, () => {
+  assert_throws_dom(code, () => {
     try {
       func.call(this);
     } catch(e) {
@@ -66,9 +69,15 @@
     w.onerror = old_onerror;
   }
   assert_equals(errors.length, 1, 'only one error should have been reported');
-  // assert_throws has an error expectation matcher that can only be
-  // accessed by throwing the error
-  assert_throws(expected_error, () => { throw errors[0]; });
+  // The testharness.js methods have error expectation matchers that can only be
+  // accessed by throwing the error.
+  if (typeof(expected_error) == 'string') {
+    assert_throws_dom(expected_error, () => { throw errors[0]; });
+  } else if (typeof(expected_error) == 'function') {
+    assert_throws_js(expected_error, () => { throw errors[0]; });
+  } else {
+    assert_throws_exactly(expected_error, () => { throw errors[0]; });
+  }
 }
 
 // Asserts that func synchronously invokes the error event handler in w
diff --git a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
index c42401f..04b5a3f 100644
--- a/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
+++ b/third_party/blink/web_tests/external/WPT_BASE_MANIFEST_7.json
@@ -522795,7 +522795,7 @@
    "testharness"
   ],
   "web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html": [
-   "67addc05476fba7107fcc97b6783180093daf96f",
+   "9bcc042a8fed318be44d09fd2436cd97ba9c4086",
    "testharness"
   ],
   "web-animations/interfaces/DocumentTimeline/constructor.html": [
@@ -522903,7 +522903,7 @@
    "support"
   ],
   "web-animations/testcommon.js": [
-   "811fe784c2635546c8bd1d6befc276d1cfbfad9a",
+   "1eb24f658c2348171b386f36dcc1b824ad236fbc",
    "support"
   ],
   "web-animations/timing-model/animation-effects/active-time.html": [
@@ -523051,7 +523051,7 @@
    "support"
   ],
   "web-animations/timing-model/timelines/update-and-send-events-replacement.html": [
-   "fced6ead657cc7a37110b156efe79b4d6cf63ccb",
+   "04389a9e2705e006686c53ac0897e0047cb61b90",
    "testharness"
   ],
   "web-animations/timing-model/timelines/update-and-send-events.html": [
diff --git a/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-size-002.html b/third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-size-001.html
similarity index 100%
rename from third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-size-002.html
rename to third_party/blink/web_tests/external/wpt/css/css-flexbox/flex-minimum-size-001.html
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
index 468861a..9493a64 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-dedicated-worker.https.html
@@ -4,6 +4,7 @@
   <title>
     Check COEP report are send for CacheStorage requests in DedicatedWorker
   </title>
+  <meta name="timeout" content="long">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script src="/common/get-host-info.sub.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
index a6a2c355..d5ae757 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-document.https.html
@@ -4,6 +4,7 @@
   <title>
     Check COEP report are send for CacheStorage requests in Document.
   </title>
+  <meta name="timeout" content="long">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script src="/common/get-host-info.sub.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
index 2cbdb21..0b13de865 100644
--- a/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
+++ b/third_party/blink/web_tests/external/wpt/html/cross-origin-embedder-policy/cache-storage-reporting-shared-worker.https.html
@@ -4,6 +4,7 @@
   <title>
     Check COEP report are send for CacheStorage requests in DedicatedWorker
   </title>
+  <meta name="timeout" content="long">
   <script src="/resources/testharness.js"></script>
   <script src="/resources/testharnessreport.js"></script>
   <script src="/common/get-host-info.sub.js"></script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html
index 15df330..611f49c0 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow-aspect-ratio.html
@@ -4,7 +4,7 @@
   <link rel="author" title="Chris Harrelson" href="mailto:chrishtr@chromium.org">
   <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
   <script src="/common/reftest-wait.js"></script>
-  <img id=target loading="lazy" src="resources/image.png"
+  <img id=target loading="lazy"
       width="200" height="100" style="width: 100px; height: auto; border: 1px solid black">
 <script>
   let loaded = false;
@@ -17,4 +17,5 @@
       takeScreenshot();
     }));
   };
+  target.src = "resources/image.png";
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html
index 15a3056f..13bb5953 100644
--- a/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html
+++ b/third_party/blink/web_tests/external/wpt/html/semantics/embedded-content/the-img-element/image-loading-lazy-slow.html
@@ -5,7 +5,7 @@
   <link rel="help" href="https://html.spec.whatwg.org/multipage/#the-img-element">
 
   <script src="/common/reftest-wait.js"></script>
-  <img id=target loading="lazy" src="resources/image.png"
+  <img id=target loading="lazy"
        width="330" height="254" style="border: 1px solid black">
 <script>
   let loaded = false;
@@ -18,4 +18,5 @@
       takeScreenshot();
     }));
   };
+  target.src = "resources/image.png";
 </script>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-click.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-click.tentative.html
deleted file mode 100644
index 2178863..0000000
--- a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-click.tentative.html
+++ /dev/null
@@ -1,34 +0,0 @@
-<!DOCTYPE html>
-<!--
-   Tentative due to:
-   https://github.com/whatwg/html/issues/1983
--->
-<html>
-<head>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-</head>
-<body>
-  <h1>Clicking on document sets user activation</h1>
-  <p>Click anywhere in the document.</p>
-  <script>
-    async_test(function(t) {
-      assert_false(navigator.userActivation.hasBeenActive);
-      assert_false(navigator.userActivation.isActive);
-      window.addEventListener("click", t.step_func_done(event => {
-        assert_true(navigator.userActivation.hasBeenActive);
-        assert_true(navigator.userActivation.isActive);
-
-        // Opening a window should consume the activation.
-        var win = window.open('404.html');
-        win.close();
-        assert_true(navigator.userActivation.hasBeenActive);
-        assert_false(navigator.userActivation.isActive);
-      }));
-      test_driver.click(document.body);
-    }, "Values adjust on activity");
-  </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-iframe-no-activate.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-iframe-no-activate.tentative.html
deleted file mode 100644
index 46fd1294..0000000
--- a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-iframe-no-activate.tentative.html
+++ /dev/null
@@ -1,58 +0,0 @@
-<!DOCTYPE html>
-<!--
-   Tentative due to:
-   https://github.com/whatwg/html/issues/1983
--->
-<html>
-<head>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-</head>
-<body>
-  <h1>Clicking in parent frame does not propagate state to child</h1>
-  <ol id="instructions">
-    <li>Click this instruction text.
-  </ol>
-  <iframe id="child" width="200" height="200"
-          src="resources/child-one.html">
-  </iframe>
-  <script>
-    async_test(function(t) {
-      var child = document.getElementById("child");
-      assert_false(navigator.userActivation.isActive);
-      assert_false(navigator.userActivation.hasBeenActive);
-      window.addEventListener("message", t.step_func(event => {
-        var msg = JSON.parse(event.data);
-        if (msg.type == 'child-one-loaded') {
-          // Child's states should be false after load.
-          assert_false(msg.isActive);
-          assert_false(msg.hasBeenActive);
-
-          // Click in parent document.
-          test_driver.click(document.getElementById('instructions'));
-        } else if (msg.type == 'child-one-report') {
-          // Only the transient state in child should be false after consumption below.
-          assert_false(msg.isActive);
-          assert_true(msg.hasBeenActive);
-          t.done();
-        }
-      }));
-      window.addEventListener("click", t.step_func(event => {
-        assert_true(navigator.userActivation.isActive);
-        assert_true(navigator.userActivation.hasBeenActive);
-
-        // Opening a window should consume the activation.
-        var win = window.open('404.html');
-        win.close();
-        assert_false(navigator.userActivation.isActive);
-        assert_true(navigator.userActivation.hasBeenActive);
-
-        // Ask child to report its state.
-        child.contentWindow.postMessage(JSON.stringify({"type": "report"}), '*');
-      }));
-    }, "Values adjust on activity");
-  </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-iframe.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-iframe.tentative.html
deleted file mode 100644
index 91fac9b..0000000
--- a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-iframe.tentative.html
+++ /dev/null
@@ -1,52 +0,0 @@
-<!DOCTYPE html>
-<!--
-   Tentative due to:
-   https://github.com/whatwg/html/issues/1983
--->
-<html>
-<head>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-</head>
-<body>
-  <h1>Clicking in iframe has activation state in child</h1>
-  <ol id="instructions">
-    <li>Click inside the yellow area.
-  </ol>
-  <iframe id="child" width="200" height="200"
-          src="resources/child-one.html">
-  </iframe>
-  <script>
-    async_test(function(t) {
-      var child = document.getElementById("child");
-      assert_false(navigator.userActivation.isActive);
-      assert_false(navigator.userActivation.hasBeenActive);
-      window.addEventListener("message", t.step_func(event => {
-        var msg = JSON.parse(event.data);
-        if (msg.type == 'child-one-loaded') {
-          // values have false after load
-          assert_false(msg.isActive);
-          assert_false(msg.hasBeenActive);
-          test_driver.click(child);
-        } else if (msg.type == 'child-one-clicked') {
-          // values have activation state on click
-          assert_true(navigator.userActivation.isActive);
-          assert_true(navigator.userActivation.hasBeenActive);
-          assert_true(msg.isActive);
-          assert_true(msg.hasBeenActive);
-          child.src = "resources/child-two.html";
-        } else if (msg.type == 'child-two-loaded') {
-          // values are reset after navigation
-          assert_true(navigator.userActivation.isActive);
-          assert_true(navigator.userActivation.hasBeenActive);
-          assert_false(msg.isActive);
-          assert_false(msg.hasBeenActive);
-          t.done();
-        }
-      }));
-    }, "Values adjust on activity");
-  </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-setTimeout.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-setTimeout.tentative.html
deleted file mode 100644
index a8eba38..0000000
--- a/third_party/blink/web_tests/external/wpt/html/user-activation/activation-api-setTimeout.tentative.html
+++ /dev/null
@@ -1,36 +0,0 @@
-<!DOCTYPE html>
-<!--
-   Tentative due to:
-    https://github.com/whatwg/html/issues/1983
--->
-<html>
-<head>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="/resources/testdriver.js"></script>
-<script src="/resources/testdriver-vendor.js"></script>
-</head>
-<body>
-  <h1>Clicking on document sets user activation even after setTimeout</h1>
-  <p>Click anywhere in the document.</p>
-  <script>
-    async_test(function(t) {
-      assert_false(navigator.userActivation.hasBeenActive);
-      assert_false(navigator.userActivation.isActive);
-      window.addEventListener("click", event => {
-        t.step_timeout(() => {
-          assert_true(navigator.userActivation.hasBeenActive);
-          assert_true(navigator.userActivation.isActive);
-
-          // Opening a window should consume the activation.
-          window.open('404.html');
-          assert_true(navigator.userActivation.hasBeenActive);
-          assert_false(navigator.userActivation.isActive);
-          t.done();
-        }, 0);
-      });
-      test_driver.click(document.body);
-    }, "Values adjust on activity");
-  </script>
-</body>
-</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/basic.html b/third_party/blink/web_tests/external/wpt/html/user-activation/basic.html
new file mode 100644
index 0000000..fe8cebc
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/basic.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/resources/testdriver.js"></script>
+  <script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+  <h1>Basic user activation test</h1>
+  <p>Tests that a popup is allowed with user activation.</p>
+  <ol id="instructions">
+    <li>Click anywhere in the document.
+  </ol></body>
+  <script>
+    let test_w_click = async_test("Popup with click");
+
+    window.addEventListener("click", () => {
+        test_w_click.step(() => {
+            let win = window.open();
+            assert_true(!!win);
+            win.close();
+        });
+        test_w_click.done();
+    });
+
+    test_driver.click(document.body);
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/chained-setTimeout.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/chained-setTimeout.tentative.html
new file mode 100644
index 0000000..133f137
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/chained-setTimeout.tentative.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+   https://github.com/whatwg/html/issues/1983
+-->
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/resources/testdriver.js"></script>
+  <script src="/resources/testdriver-vendor.js"></script>
+  <script>
+    const max_call_depth = 3;
+    let initial_async_tests = {};
+    let final_async_tests = {};
+
+    function testInitialStates(depth) {
+        assert_true(1 <= depth && depth <= max_call_depth);
+
+        initial_async_tests[depth].step_timeout(() => {
+            assert_false(navigator.userActivation.isActive);
+            assert_false(navigator.userActivation.hasBeenActive);
+            initial_async_tests[depth].done();
+
+            if (depth < max_call_depth)
+                testInitialStates(depth+1);
+        }, 10);
+    }
+
+    function testFinalStates(depth) {
+        assert_true(1 <= depth && depth <= max_call_depth);
+
+        final_async_tests[depth].step_timeout(() => {
+            assert_true(navigator.userActivation.isActive);
+            assert_true(navigator.userActivation.hasBeenActive);
+            final_async_tests[depth].done();
+
+            if (depth < max_call_depth)
+                testFinalStates(depth+1);
+        }, 10)
+    }
+
+    function run() {
+        for (let i = 1; i <= max_call_depth; i++) {
+            initial_async_tests[i] = async_test("Call-depth=" + i + " initial state");
+            final_async_tests[i] =   async_test("Call-depth=" + i + " final state");
+        }
+
+        testInitialStates(1);
+
+        window.addEventListener("click", event => {
+            testFinalStates(1);
+        });
+        test_driver.click(document.body);
+    }
+  </script>
+</head>
+<body onload="run()">
+  <h1>User activation state in chained setTimeout calls</h1>
+  <p>Tests that user activation state is visible in arbitrary call depth of setTimeout.</p>
+  <ol id="instructions">
+    <li>Click anywhere in the document.
+  </ol></body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/consumption-crossorigin.sub.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/consumption-crossorigin.sub.tentative.html
new file mode 100644
index 0000000..01e4d35a4
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/consumption-crossorigin.sub.tentative.html
@@ -0,0 +1,131 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+   https://github.com/whatwg/html/issues/1983
+-->
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/resources/testdriver.js"></script>
+  <script src="/resources/testdriver-vendor.js"></script>
+  <script>
+    // Frame layout:
+    // top=origin0:this-file [
+    //   child1=origin1:child-one.html,
+    //   child-xo=origin2:consumption-crossorigin-child.html [
+    //     gchild=origin3:child-two.html
+    //   ]
+    // ]
+    let test_parent_initial = async_test("Parent frame initial state");
+    let test_parent_final = async_test("Parent frame final state");
+
+    let test_child1_initial = async_test("Child1 frame initial state");
+    let test_child1_final = async_test("Child1 frame final state");
+
+    let test_child_xo_initial = async_test("Child2 frame initial state");
+    let test_child_xo_final = async_test("Child2 frame final state");
+
+    let test_gchild_initial = async_test("Grandchild frame initial state");
+    let test_gchild_final = async_test("Grand child frame final state");
+
+    let num_children_to_load = 3;
+    let num_children_to_report = 3;
+
+    function finishLoadPhase() {
+        test_parent_initial.step(() => {
+            assert_true(num_children_to_load == 0);
+            assert_false(navigator.userActivation.isActive);
+            assert_false(navigator.userActivation.hasBeenActive);
+        });
+        test_parent_initial.done();
+
+        test_driver.click(document.getElementById("child1"));
+        // The click at "child-xo" happens after receiving "child-one-clicked" msg.
+    }
+
+    function finishReportPhase() {
+        test_parent_final.step(() => {
+            assert_true(num_children_to_report == 0);
+            assert_false(navigator.userActivation.isActive);
+            assert_true(navigator.userActivation.hasBeenActive);
+        });
+        test_parent_final.done();
+        // End of all tests.
+    }
+
+    window.addEventListener("message", event => {
+        var msg = JSON.parse(event.data);
+
+        if (msg.type == 'child-one-loaded') {
+            test_child1_initial.step(() => {
+                assert_false(msg.isActive);
+                assert_false(msg.hasBeenActive);
+            });
+            test_child1_initial.done();
+        } else if (msg.type == 'child-crossorigin-loaded') {
+            test_child_xo_initial.step(() => {
+                assert_false(msg.isActive);
+                assert_false(msg.hasBeenActive);
+            });
+            test_child_xo_initial.done();
+        } else if (msg.type == 'child-two-loaded') {
+            test_gchild_initial.step(() => {
+                assert_false(msg.isActive);
+                assert_false(msg.hasBeenActive);
+            });
+            test_gchild_initial.done();
+        } else if (msg.type == 'child-one-clicked') {
+            test_driver.click(document.getElementById("child-xo"));
+        } else if (msg.type == 'child-one-report') {
+            test_child1_final.step(() => {
+                assert_false(msg.isActive);
+                assert_true(msg.hasBeenActive);
+            });
+            test_child1_final.done();
+        } else if (msg.type == 'child-crossorigin-report') {
+            // This msg was triggered by a user click followed by a window.open().
+            test_child_xo_final.step(() => {
+                assert_false(msg.isActive);
+                assert_true(msg.hasBeenActive);
+            });
+            test_child_xo_final.done();
+
+            // Ask remaining frames to report states.
+            let ask_report = JSON.stringify({"type": "report"});
+            frames[0].postMessage(ask_report, "*");
+            frames[1].frames[0].postMessage(ask_report, "*");
+        } else if (msg.type == 'child-two-report') {
+            test_gchild_final.step(() => {
+                assert_false(msg.isActive);
+                assert_false(msg.hasBeenActive);
+            });
+            test_gchild_final.done();
+        }
+
+        // Phase switching.
+        if (msg.type.endsWith("-loaded")) {
+            if (--num_children_to_load == 0)
+                finishLoadPhase();
+        } else if (msg.type.endsWith("-report")) {
+            if (--num_children_to_report == 0)
+                finishReportPhase();
+        }
+    });
+  </script>
+</head>
+<body>
+  <h1>User activation consumption across cross-origin frame boundary</h1>
+  <p>Tests that user activation consumption resets the transient states in all cross-origin frames.</p>
+  <ol id="instructions">
+    <li>Click anywhere on the yellow area.
+    <li>Click anywhere on the green area (child frame).
+  </ol>
+  <iframe id="child1" width="300px" height="40px"
+          src="http://{{domains[www1]}}:{{ports[http][0]}}/html/user-activation/resources/child-one.html">
+  </iframe>
+  <iframe id="child-xo" width="300px" height="140px"
+          src="http://{{domains[www2]}}:{{ports[http][0]}}/html/user-activation/resources/consumption-crossorigin-child.sub.html">
+  </iframe>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/consumption-sameorigin.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/consumption-sameorigin.tentative.html
new file mode 100644
index 0000000..140642c
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/consumption-sameorigin.tentative.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+   https://github.com/whatwg/html/issues/1983
+-->
+<html>
+<head>
+  <script src="/resources/testharness.js"></script>
+  <script src="/resources/testharnessreport.js"></script>
+  <script src="/resources/testdriver.js"></script>
+  <script src="/resources/testdriver-vendor.js"></script>
+  <script>
+    // Frame layout:
+    // top=this-file [
+    //   child1=child-one.html,
+    //   child-so=consumption-sameorigin-child.html [
+    //     gchild=child-two.html
+    //   ]
+    // ]
+    let test_parent_initial = async_test("Parent frame initial state");
+    let test_parent_final = async_test("Parent frame final state");
+
+    let test_child1_initial = async_test("Child1 frame initial state");
+    let test_child1_final = async_test("Child1 frame final state");
+
+    let test_child_so_initial = async_test("Child2 frame initial state");
+    let test_child_so_final = async_test("Child2 frame final state");
+
+    let test_gchild_initial = async_test("Grandchild frame initial state");
+    let test_gchild_final = async_test("Grand child frame final state");
+
+    let num_children_to_load = 3;
+    let num_children_to_report = 3;
+
+    function finishLoadPhase() {
+        test_parent_initial.step(() => {
+            assert_true(num_children_to_load == 0);
+            assert_false(navigator.userActivation.isActive);
+            assert_false(navigator.userActivation.hasBeenActive);
+        });
+        test_parent_initial.done();
+
+        test_driver.click(document.getElementById("child-so"));
+    }
+
+    function finishReportPhase() {
+        test_parent_final.step(() => {
+            assert_true(num_children_to_report == 0);
+            assert_false(navigator.userActivation.isActive);
+            assert_true(navigator.userActivation.hasBeenActive);
+        });
+        test_parent_final.done();
+        // End of all tests.
+    }
+
+    window.addEventListener("message", event => {
+        var msg = JSON.parse(event.data);
+
+        if (msg.type == 'child-one-loaded') {
+            test_child1_initial.step(() => {
+                assert_false(msg.isActive);
+                assert_false(msg.hasBeenActive);
+            });
+            test_child1_initial.done();
+        } else if (msg.type == 'child-sameorigin-loaded') {
+            test_child_so_initial.step(() => {
+                assert_false(msg.isActive);
+                assert_false(msg.hasBeenActive);
+            });
+            test_child_so_initial.done();
+        } else if (msg.type == 'child-two-loaded') {
+            test_gchild_initial.step(() => {
+                assert_false(msg.isActive);
+                assert_false(msg.hasBeenActive);
+            });
+            test_gchild_initial.done();
+        } else if (msg.type == 'child-one-report') {
+            test_child1_final.step(() => {
+                assert_false(msg.isActive);
+                assert_true(msg.hasBeenActive);
+            });
+            test_child1_final.done();
+        } else if (msg.type == 'child-sameorigin-report') {
+            // This msg was triggered by a user click followed by a window.open().
+            test_child_so_final.step(() => {
+                assert_false(msg.isActive);
+                assert_true(msg.hasBeenActive);
+            });
+            test_child_so_final.done();
+
+            // Ask remaining frames to report states.
+            let ask_report = JSON.stringify({"type": "report"});
+            frames[0].postMessage(ask_report, "*");
+            frames[1].frames[0].postMessage(ask_report, "*");
+        } else if (msg.type == 'child-two-report') {
+            test_gchild_final.step(() => {
+                assert_false(msg.isActive);
+                assert_true(msg.hasBeenActive);
+            });
+            test_gchild_final.done();
+        }
+
+        // Phase switching.
+        if (msg.type.endsWith("-loaded")) {
+            if (--num_children_to_load == 0)
+                finishLoadPhase();
+        } else if (msg.type.endsWith("-report")) {
+            if (--num_children_to_report == 0)
+                finishReportPhase();
+        }
+    });
+  </script>
+</head>
+<body>
+  <h1>User activation consumption across same-origin frame boundary</h1>
+  <p>Tests that user activation consumption resets the transient states in all same-origin frames.</p>
+  <ol id="instructions">
+    <li>Click anywhere on the green area (child frame).
+  </ol>
+  <iframe id="child1" width="300px" height="40px"
+          src="resources/child-one.html">
+  </iframe>
+  <iframe id="child-so" width="300px" height="140px"
+          src="resources/consumption-sameorigin-child.html">
+  </iframe>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html
new file mode 100644
index 0000000..5839836
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/navigation-state-reset-crossorigin.sub.tentative.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+   https://github.com/whatwg/html/issues/1983
+-->
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+  <h1>Post-navigation activation state in child</h1>
+  <p>Tests that navigating a cross-origin child frame resets its activation states.</p>
+  <ol id="instructions">
+    <li>Click inside the yellow area.
+  </ol>
+  <iframe id="child" width="200" height="50"
+          src="http://{{domains[www1]}}:{{ports[http][0]}}/html/user-activation/resources/child-one.html">
+  </iframe>
+  <script>
+    async_test(function(t) {
+      var child = document.getElementById("child");
+      window.addEventListener("message", t.step_func(event => {
+          var msg = JSON.parse(event.data);
+          if (msg.type == 'child-one-loaded') {
+              assert_false(navigator.userActivation.isActive);
+              assert_false(navigator.userActivation.hasBeenActive);
+              assert_false(msg.isActive);
+              assert_false(msg.hasBeenActive);
+
+              test_driver.click(child);
+          } else if (msg.type == 'child-one-clicked') {
+              assert_true(navigator.userActivation.isActive);
+              assert_true(navigator.userActivation.hasBeenActive);
+              assert_true(msg.isActive);
+              assert_true(msg.hasBeenActive);
+
+              child.src = "http://{{domains[www2]}}:{{ports[http][0]}}/html/user-activation/resources/child-two.html";
+          } else if (msg.type == 'child-two-loaded') {
+              assert_true(navigator.userActivation.isActive);
+              assert_true(navigator.userActivation.hasBeenActive);
+              assert_false(msg.isActive);
+              assert_false(msg.hasBeenActive);
+
+              t.done();
+          }
+      }));
+    }, "Post-navigation state reset.");
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/navigation-state-reset-sameorigin.tentative.html b/third_party/blink/web_tests/external/wpt/html/user-activation/navigation-state-reset-sameorigin.tentative.html
new file mode 100644
index 0000000..144d7d4b
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/navigation-state-reset-sameorigin.tentative.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<!--
+   Tentative due to:
+   https://github.com/whatwg/html/issues/1983
+-->
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+</head>
+<body>
+  <h1>Post-navigation activation state in child</h1>
+  <p>Tests that navigating a same-origin child frame resets its activation states.</p>
+  <ol id="instructions">
+    <li>Click inside the yellow area.
+  </ol>
+  <iframe id="child" width="200" height="50"
+          src="resources/child-one.html">
+  </iframe>
+  <script>
+    async_test(function(t) {
+      var child = document.getElementById("child");
+      window.addEventListener("message", t.step_func(event => {
+          var msg = JSON.parse(event.data);
+          if (msg.type == 'child-one-loaded') {
+              assert_false(navigator.userActivation.isActive);
+              assert_false(navigator.userActivation.hasBeenActive);
+              assert_false(msg.isActive);
+              assert_false(msg.hasBeenActive);
+
+              test_driver.click(child);
+          } else if (msg.type == 'child-one-clicked') {
+              assert_true(navigator.userActivation.isActive);
+              assert_true(navigator.userActivation.hasBeenActive);
+              assert_true(msg.isActive);
+              assert_true(msg.hasBeenActive);
+
+              child.src = "resources/child-two.html";
+          } else if (msg.type == 'child-two-loaded') {
+              assert_true(navigator.userActivation.isActive);
+              assert_true(navigator.userActivation.hasBeenActive);
+              assert_false(msg.isActive);
+              assert_false(msg.hasBeenActive);
+
+              t.done();
+          }
+      }));
+    }, "Post-navigation state reset.");
+  </script>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/resources/consumption-crossorigin-child.sub.html b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/consumption-crossorigin-child.sub.html
new file mode 100644
index 0000000..c9f37ac
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/consumption-crossorigin-child.sub.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script>
+    window.top.postMessage(JSON.stringify({
+        "type": "child-crossorigin-loaded",
+        "isActive": navigator.userActivation.isActive,
+        "hasBeenActive": navigator.userActivation.hasBeenActive
+    }), "*");
+
+    window.addEventListener("click", event => {
+        window.open().close();
+
+        window.top.postMessage(JSON.stringify({
+            "type": "child-crossorigin-report",
+            "isActive": navigator.userActivation.isActive,
+            "hasBeenActive": navigator.userActivation.hasBeenActive
+        }), "*");
+    });
+  </script>
+</head>
+<body style="background: lightgreen;">
+  <!-- The midpoint of this frame should be outside the grandchild frame. -->
+  <div style="height: 75px;">Cross-origin child frame</div>
+  <iframe id="child2" width="270px" height="30px"
+          src="http://{{domains[www]}}:{{ports[http][0]}}/html/user-activation/resources/child-two.html">
+  </iframe>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/html/user-activation/resources/consumption-sameorigin-child.html b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/consumption-sameorigin-child.html
new file mode 100644
index 0000000..9e421fc0
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/html/user-activation/resources/consumption-sameorigin-child.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <script>
+    window.top.postMessage(JSON.stringify({
+        "type": "child-sameorigin-loaded",
+        "isActive": navigator.userActivation.isActive,
+        "hasBeenActive": navigator.userActivation.hasBeenActive
+    }), "*");
+
+    window.addEventListener("click", event => {
+        window.open().close();
+
+        window.top.postMessage(JSON.stringify({
+            "type": "child-sameorigin-report",
+            "isActive": navigator.userActivation.isActive,
+            "hasBeenActive": navigator.userActivation.hasBeenActive
+        }), "*");
+    });
+  </script>
+</head>
+<body style="background: lightgreen;">
+  <!-- The midpoint of this frame should be outside the grandchild frame. -->
+  <div style="height: 75px;">Same-origin child frame</div>
+  <iframe id="child2" width="270px" height="30px"
+          src="child-two.html">
+  </iframe>
+</body>
+</html>
diff --git a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html
index 6cb1b8b4..0a1d7ef 100644
--- a/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html
+++ b/third_party/blink/web_tests/external/wpt/mathml/presentation-markup/fractions/frac-linethickness-002.html
@@ -56,6 +56,12 @@
           assert_approx_equals(LineThickness("namedspace"), defaultRuleThickness, epsilon);
         }, "Named space");
 
+        test(function() {
+          assert_true(MathMLFeatureDetection.has_mspace());
+          /* Calc() expressions are invalid in MathML Core. */
+          assert_approx_equals(LineThickness("calc"), defaultRuleThickness, epsilon);
+        }, "Calc() expression");
+
         done();
       }
     </script>
@@ -85,5 +91,11 @@
         <mspace width="20px" height="10px" style="background: cyan"></mspace>
       </mfrac>
     </math>
+    <math>
+      <mfrac id="calc" linethickness="calc(20px)">
+        <mspace width="20px" height="10px" style="background: blue"></mspace>
+        <mspace width="20px" height="10px" style="background: cyan"></mspace>
+      </mfrac>
+    </math>
   </body>
 </html>
diff --git a/third_party/blink/web_tests/external/wpt/resource-timing/idlharness.any.js b/third_party/blink/web_tests/external/wpt/resource-timing/idlharness.any.js
index a7542f1..aa860d3 100644
--- a/third_party/blink/web_tests/external/wpt/resource-timing/idlharness.any.js
+++ b/third_party/blink/web_tests/external/wpt/resource-timing/idlharness.any.js
@@ -1,5 +1,6 @@
 // META: script=/resources/WebIDLParser.js
 // META: script=/resources/idlharness.js
+// META: timeout=long
 
 'use strict';
 
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html
index 67addc0..9bcc042 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/interfaces/DocumentOrShadowRoot/getAnimations.html
@@ -68,7 +68,7 @@
 
 promise_test(async t => {
   const iframe = document.createElement('iframe');
-  insertFrameAndAwaitLoad(t, iframe, document)
+  await insertFrameAndAwaitLoad(t, iframe, document)
 
   const div = createDiv(t, iframe.contentDocument)
   const effect = new KeyframeEffect(div, null, 100 * MS_PER_SEC);
@@ -88,8 +88,8 @@
   const iframe1 = document.createElement('iframe');
   const iframe2 = document.createElement('iframe');
 
-  insertFrameAndAwaitLoad(t, iframe1, document);
-  insertFrameAndAwaitLoad(t, iframe2, document);
+  await insertFrameAndAwaitLoad(t, iframe1, document);
+  await insertFrameAndAwaitLoad(t, iframe2, document);
 
   const div_frame1 = createDiv(t, iframe1.contentDocument)
   const div_main_frame = createDiv(t)
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js b/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
index 811fe784..1eb24f6 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
+++ b/third_party/blink/web_tests/external/wpt/web-animations/testcommon.js
@@ -180,12 +180,12 @@
   });
 }
 
-async function insertFrameAndAwaitLoad(test, iframe, document) {
+async function insertFrameAndAwaitLoad(test, iframe, doc) {
   const eventWatcher = new EventWatcher(test, iframe, ['load']);
   const event_promise = eventWatcher.wait_for('load');
 
-  document.body.appendChild(iframe);
-  test.add_cleanup(() => { document.body.removeChild(iframe); });
+  doc.body.appendChild(iframe);
+  test.add_cleanup(() => { doc.body.removeChild(iframe); });
 
   await event_promise;
 }
diff --git a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html
index fced6ead..04389a9 100644
--- a/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html
+++ b/third_party/blink/web_tests/external/wpt/web-animations/timing-model/timelines/update-and-send-events-replacement.html
@@ -989,14 +989,14 @@
 
 promise_test(async t => {
   const outerIframe = document.createElement('iframe');
-  insertFrameAndAwaitLoad(t, outerIframe, document);
   outerIframe.width = 10;
   outerIframe.height = 10;
+  await insertFrameAndAwaitLoad(t, outerIframe, document);
 
   const innerIframe = document.createElement('iframe');
-  insertFrameAndAwaitLoad(t, innerIframe, outerIframe.contentDocument);
   innerIframe.width = 10;
   innerIframe.height = 10;
+  await insertFrameAndAwaitLoad(t, innerIframe, outerIframe.contentDocument);
 
   const div = createDiv(t, innerIframe.contentDocument);
 
diff --git a/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html
new file mode 100644
index 0000000..84d9f18
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webaudio/the-audio-api/the-delaynode-interface/maxdelay-rounding.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>
+      Test DelayNode when maxDelayTime requires rounding
+    </title>
+    <script src="/resources/testharness.js"></script>
+    <script src="/resources/testharnessreport.js"></script>
+    <script src="/webaudio/resources/audit-util.js"></script>
+    <script src="/webaudio/resources/audit.js"></script>
+  </head>
+  <body>
+    <script id="layout-test-code">
+      let sampleRate = 44100;
+      let inputLengthSeconds = 1;
+      let renderLengthSeconds = 2;
+
+      // Delay for one second plus 0.4 of a sample frame, to test that
+      // DelayNode is properly rounding up when calculating its buffer
+      // size (crbug.com/1065110).
+      let delayTimeSeconds = 1 + 0.4 / sampleRate;
+
+      let audit = Audit.createTaskRunner();
+
+      audit.define(
+          {
+            label: 'maxdelay-rounding',
+            description: 'Test DelayNode when maxDelayTime requires rounding',
+          },
+          (task, should) => {
+            let context = new OfflineAudioContext({
+                numberOfChannels: 1,
+                length: sampleRate * renderLengthSeconds,
+                sampleRate: sampleRate,
+            });
+
+            // Create a constant source to use as input.
+            let src = new ConstantSourceNode(context);
+
+            // Create a DelayNode to delay for delayTimeSeconds.
+            let delay = new DelayNode(context, {
+                maxDelayTime: delayTimeSeconds,
+                delayTime: delayTimeSeconds,
+            });
+
+            src.connect(delay).connect(context.destination);
+
+            src.start();
+            context.startRendering()
+                .then(renderedBuffer => {
+                  let renderedData = renderedBuffer.getChannelData(0);
+
+                  // The first delayTimeSeconds of output should be silent.
+                  let expectedSilentFrames = Math.floor(
+                      delayTimeSeconds * sampleRate);
+
+                  should(
+                      renderedData.slice(0, expectedSilentFrames),
+                      `output[0:${expectedSilentFrames - 1}]`)
+                      .beConstantValueOf(0);
+
+                  // The rest should be non-silent: that is, there should
+                  // be at least one non-zero sample.  (Any reasonable
+                  // interpolation algorithm will make all these samples
+                  // non-zero, but I don't think that's guaranteed by the
+                  // spec, so we use a conservative test for now.)
+                  should(
+                      renderedData.slice(expectedSilentFrames),
+                      `output[${expectedSilentFrames}:]`)
+                      .notBeConstantValueOf(0);
+                })
+                .then(() => task.done());
+          });
+
+      audit.run();
+    </script>
+  </body>
+</html>
diff --git a/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html b/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html
index 6572e35..01b607e 100644
--- a/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html
+++ b/third_party/blink/web_tests/fast/events/mouse-cursor-change-after-image-load.html
@@ -18,8 +18,6 @@
 cursorTest.step(function() {
     assert_not_equals(window.eventSender, undefined, 'This test requires eventSender.');
 
-    assert_equals(internals.getCurrentCursorInfo(), "type=Pointer");
-
     var target = document.getElementById('target');
     var rect = target.getBoundingClientRect();
     eventSender.mouseMoveTo(rect.left + 3, rect.top + 3);
@@ -34,10 +32,8 @@
     }
 
     function testSetCursorImage(url, expectedCursorInfo, complete) {
-        const currentCursorInfo = internals.getCurrentCursorInfo();
         target.style.cursor = `url(${url}),auto`;
-        waitForCondition(() => internals.getCurrentCursorInfo() != currentCursorInfo, cursorTest.step_func(() => {
-            assert_equals(internals.getCurrentCursorInfo(), expectedCursorInfo);
+        waitForCondition(() => internals.getCurrentCursorInfo() != expectedCursorInfo, cursorTest.step_func(() => {
             complete();
         }));
     }
diff --git a/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/composited-iframe-scroll-repaint-expected.txt b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/composited-iframe-scroll-repaint-expected.txt
new file mode 100644
index 0000000..f3be4c37
--- /dev/null
+++ b/third_party/blink/web_tests/flag-specific/composite-after-paint/paint/invalidation/scroll/composited-iframe-scroll-repaint-expected.txt
@@ -0,0 +1,55 @@
+{
+  "layers": [
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [800, 600],
+      "contentsOpaque": true,
+      "backgroundColor": "#FFFFFF"
+    },
+    {
+      "name": "Scrolling background of LayoutView #document",
+      "bounds": [300, 516],
+      "contentsOpaque": true,
+      "backgroundColor": "#EEEEEE",
+      "transform": 2
+    },
+    {
+      "name": "LayoutNGBlockFlow BODY",
+      "bounds": [284, 500],
+      "drawsContent": false,
+      "transform": 3
+    }
+  ],
+  "transforms": [
+    {
+      "id": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [10, 10, 0, 1]
+      ]
+    },
+    {
+      "id": 2,
+      "parent": 1,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [0, -20, 0, 1]
+      ]
+    },
+    {
+      "id": 3,
+      "parent": 2,
+      "transform": [
+        [1, 0, 0, 0],
+        [0, 1, 0, 0],
+        [0, 0, 1, 0],
+        [8, 8, 0, 1]
+      ]
+    }
+  ]
+}
+
diff --git a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-075.html b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-075.html
index 4c8b128..a31a30f 100644
--- a/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-075.html
+++ b/third_party/blink/web_tests/wpt_internal/display-lock/css-subtree-visibility/subtree-visibility-075.html
@@ -4,18 +4,21 @@
 <title>Subtree Visibility: accessibility focus</title>
 <link rel="author" title="Aaron Leventhal" href="mailto:aleventhal@chromium.org">
 <link rel="help" href="https://github.com/WICG/display-locking">
-<link rel="match" href="container-ref.html">
-<meta name="assert" content="subtree-visibility auto subtrees are exposed by accessibility focus">
+<meta name="assert" content="subtree-visibility auto accessible object subtrees are not destroyed after accessing name">
 
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
 <style>
+.spacer {
+  height: 3000px;
+}
 .auto {
   subtree-visibility: auto;
 }
 </style>
 
-<div id="hidden" style="subtree-visibility: auto">
+<div class=spacer></div>
+<div id="hidden" class=auto>
   foo
   <div id="child" tabindex="0">
     bar
@@ -31,9 +34,9 @@
   const hiddenEl = document.getElementById("hidden");
   let axHidden = axElementById("hidden");
   t.step(() => {
-    assert_equals(axHidden.childrenCount, 3, "Child count after acquire" );
+    assert_equals(axHidden.childrenCount, 3, "Child count when hidden" );
     axHidden.childAtIndex(1).name;   // Referring to the name should have no effect on the number of children.
-    assert_equals(axHidden.childrenCount, 3, "Child count after acquire, and accessing name of first child" );
+    assert_equals(axHidden.childrenCount, 3, "Child count when hidden, and accessing name of first child" );
     t.done();
   });
 }, "Accessiblility name calculation on child of subtree-visibility: auto does not destroy tree");
diff --git a/tools/chrome_proxy/webdriver/subresource_redirect.py b/tools/chrome_proxy/webdriver/subresource_redirect.py
index 4fe16307..514d848 100644
--- a/tools/chrome_proxy/webdriver/subresource_redirect.py
+++ b/tools/chrome_proxy/webdriver/subresource_redirect.py
@@ -12,16 +12,32 @@
 
 class SubresourceRedirect(IntegrationTest):
 
+  def enableSubresourceRedirectFeature(self, test_driver):
+    test_driver.EnableChromeFeature('SubresourceRedirect<SubresourceRedirect')
+    test_driver.AddChromeArg('--force-fieldtrials=SubresourceRedirect/Enabled')
+    test_driver.AddChromeArg(
+        '--force-fieldtrial-params='
+        'SubresourceRedirect.Enabled:enable_lite_page_redirect/true')
+    test_driver.EnableChromeFeature('OptimizationHints')
+    test_driver.EnableChromeFeature('OptimizationHintsFetching')
+    test_driver.EnableChromeFeature(
+        'OptimizationHintsFetchingAnonymousDataConsent')
+    test_driver.AddChromeArg('--enable-spdy-proxy-auth')
+    test_driver.AddChromeArg('--dont-require-litepage-redirect-infobar')
+
   # Verifies that image subresources on a page have been returned
   # from the compression server.
   @ChromeVersionEqualOrAfterM(77)
   def testCompressImage(self):
     with TestDriver() as test_driver:
-      test_driver.AddChromeArg('--enable-subresource-redirect')
-      test_driver.LoadURL('https://check.googlezip.net/static/index.html')
+      self.enableSubresourceRedirectFeature(test_driver)
+      test_driver.LoadURL(
+          'https://check.googlezip.net/static/image_delayed_load.html')
+
+      test_driver.SleepUntilHistogramHasEntry(
+          'SubresourceRedirect.CompressionAttempt.ServerResponded')
 
       image_responses = 0
-
       for response in test_driver.GetHTTPResponses():
         content_type = ''
         if 'content-type' in response.response_headers:
@@ -38,7 +54,7 @@
   @ChromeVersionEqualOrAfterM(77)
   def testOnRedirectImage(self):
     with TestDriver() as test_driver:
-      test_driver.AddChromeArg('--enable-subresource-redirect')
+      self.enableSubresourceRedirectFeature(test_driver)
       # Image compression server returns a 307 for all images on this webpage.
       test_driver.LoadURL(
         'https://testsafebrowsing.appspot.com/s/image_small.html')
@@ -66,7 +82,7 @@
   @ChromeVersionEqualOrAfterM(77)
   def testNoCompressNonImage(self):
     with TestDriver() as test_driver:
-      test_driver.AddChromeArg('--enable-subresource-redirect')
+      self.enableSubresourceRedirectFeature(test_driver)
       test_driver.LoadURL('https://check.googlezip.net/testvideo.html')
 
       image_responses = 0
@@ -87,7 +103,7 @@
   @ChromeVersionEqualOrAfterM(77)
   def testNoCompressNonHTTPS(self):
     with TestDriver() as test_driver:
-      test_driver.AddChromeArg('--enable-subresource-redirect')
+      self.enableSubresourceRedirectFeature(test_driver)
       test_driver.LoadURL('http://check.googlezip.net/static/index.html')
 
       image_responses = 0
diff --git a/tools/determinism/compare_build_artifacts.py b/tools/determinism/compare_build_artifacts.py
index e586e405..ed806fa9 100755
--- a/tools/determinism/compare_build_artifacts.py
+++ b/tools/determinism/compare_build_artifacts.py
@@ -63,19 +63,22 @@
 
 
 def get_files_to_compare_using_isolate(build_dir):
-  # First, find all .runtime_deps files in build_dir.
-  runtime_deps_files = glob.glob(os.path.join(build_dir, '*.runtime_deps'))
+  # First, find all .isolate files in build_dir.
+  # TODO(maruel): https://crbug.com/972075 Extract targets from mb.
+  isolates = glob.glob(os.path.join(build_dir, '*.isolate'))
 
   # Then, extract their contents.
   ret_files = set()
 
-  for runtime_deps_file in runtime_deps_files:
-    with open(runtime_deps_file) as f:
-      for runtime_dep in f:
-        runtime_dep = runtime_dep.rstrip()
-        normalized_path = os.path.normpath(os.path.join(build_dir, runtime_dep))
+  for isolate in isolates:
+    with open(isolate) as f:
+      isolate_contents = ast.literal_eval(f.read())
+      isolate_files = isolate_contents['variables']['files']
+      for isolate_file in isolate_files:
+        normalized_path = os.path.normpath(
+            os.path.join(build_dir, isolate_file))
 
-        # Ignore runtime dep files that are not in the build dir ... for now.
+        # Ignore isolate files that are not in the build dir ... for now.
         # If we ever move to comparing determinism of artifacts built from two
         # repositories, we'll want to get rid of this check.
         if os.path.commonprefix((normalized_path, build_dir)) != build_dir:
@@ -386,11 +389,9 @@
   parser.add_option('-r', '--recursive', action='store_true', default=False,
                     help='Indicates if the comparison should be recursive.')
   parser.add_option(
-      '--use-isolate-files',
-      action='store_true',
-      default=False,
-      help='Use .runtime_deps files in each directory to determine which '
-      'artifacts to compare.')
+      '--use-isolate-files', action='store_true', default=False,
+      help='Use .isolate files in each directory to determine which artifacts '
+           'to compare.')
 
   parser.add_option('--json-output', help='JSON file to output differences')
   parser.add_option('--ninja-path', help='path to ninja command.',
diff --git a/tools/mb/mb_config.pyl b/tools/mb/mb_config.pyl
index f075fa2..0f73e61 100644
--- a/tools/mb/mb_config.pyl
+++ b/tools/mb/mb_config.pyl
@@ -1230,7 +1230,7 @@
 
     'android_release_trybot_fastbuild': [
       'android', 'release_trybot', 'strip_debug_info', 'android_fastbuild',
-      'android_no_proguard', 
+      'android_no_proguard',
     ],
 
     'android_release_trybot_fastbuild_webview_google': [
@@ -1831,7 +1831,7 @@
     'ios_error': [ 'error'],
 
     'ios_clang_tot': [
-      'clang_tot', 'ios_device', 'ios_disable_code_signing', 'release', 'static',
+      'clang_tot', 'ios_simulator', 'ios_disable_code_signing', 'release', 'static',
     ],
 
     'ios_clang_device_tot': [
diff --git a/tools/metrics/actions/actions.xml b/tools/metrics/actions/actions.xml
index 9ec1a1e..98d9983 100644
--- a/tools/metrics/actions/actions.xml
+++ b/tools/metrics/actions/actions.xml
@@ -13785,6 +13785,13 @@
   </description>
 </action>
 
+<action name="MobileShareMenuGenerateQRCode">
+  <owner>seblalancette@chromium.org</owner>
+  <description>
+    The user pressed the generate QR code option from the share action menu.
+  </description>
+</action>
+
 <action name="MobileShareMenuReadLater">
   <owner>gambard@chromium.org</owner>
   <description>User shared to the device's Reading List.</description>
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 2d4e57f6..c5e506278 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -56257,6 +56257,38 @@
   <int value="10" label="Parent node is not a folder"/>
 </enum>
 
+<enum name="RemoteCommandExecutionStatus">
+  <int value="0" label="Not initialized"/>
+  <int value="1" label="Invalid"/>
+  <int value="2" label="Expired"/>
+  <int value="3" label="Not started"/>
+  <int value="4" label="Running"/>
+  <int value="5" label="Succeeded"/>
+  <int value="6" label="Failed"/>
+  <int value="7" label="Terminated"/>
+</enum>
+
+<enum name="RemoteCommandReceivedStatus">
+  <int value="0" label="Invalid: Invalid signature"/>
+  <int value="1" label="Invalid: Invalid (if no other error is applicable)"/>
+  <int value="2" label="Invalid: Unknown type"/>
+  <int value="3" label="Invalid: Duplicated"/>
+  <int value="4" label="Invalid: Invalid scope (user or device)"/>
+  <int value="5" label="Command echo test (should not appear)"/>
+  <int value="6" label="Reboot device command"/>
+  <int value="7" label="Screenshot command"/>
+  <int value="8" label="Set volume command"/>
+  <int value="9" label="Fetch status command"/>
+  <int value="10" label="ARC command"/>
+  <int value="11" label="Wipe users command"/>
+  <int value="12" label="Start CRD session command"/>
+  <int value="13" label="Powerwash command"/>
+  <int value="14" label="Refresh enterprise machine certificate command"/>
+  <int value="15" label="Get available diagnostic routines command"/>
+  <int value="16" label="Run diagnostic routine command"/>
+  <int value="17" label="Get diagnostic routine update command"/>
+</enum>
+
 <enum name="RemoteHungProcessTerminateReason">
   <int value="1" label="Terminate accepted by user"/>
   <int value="2" label="No visible windows found"/>
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index fe391d9..161a5a6 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -24594,7 +24594,7 @@
   </summary>
 </histogram>
 
-<histogram name="ChromeOS.UrlXattrsCount" units="units" expires_after="M83">
+<histogram name="ChromeOS.UrlXattrsCount" units="units" expires_after="M89">
   <owner>jorgelo@chromium.org</owner>
   <owner>tnagel@chromium.org</owner>
   <summary>
@@ -44630,6 +44630,56 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.DeviceRemoteCommand.Executed"
+    enum="RemoteCommandExecutionStatus" expires_after="2021-03-01">
+<!-- Name completed by histogram_suffixes name="Enterprise.RemoteCommandType". -->
+
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting status of executed device remote commands. The metric
+    includes only signed device remote commands. Reports a final status of
+    finished remote command (e.g. success, failure or termination).
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.DeviceRemoteCommand.Executed.Unsigned"
+    enum="RemoteCommandExecutionStatus" expires_after="2021-03-01">
+<!-- Name completed by histogram_suffixes name="Enterprise.RemoteCommandType". -->
+
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting status of executed device remote commands. The metric
+    includes only unsigned device remote commands. Reports a final status of
+    finished remote command (e.g. success, failure or termination).
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.DeviceRemoteCommand.Received"
+    enum="RemoteCommandReceivedStatus" expires_after="2021-03-01">
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting received device remote commands. The metric includes
+    signed device remote commands, both valid and invalid. Valid remote command
+    is counted by its type. Invalid remote command is counted by a reason why it
+    is considered invalid (e.g. unknown type or duplication).
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.DeviceRemoteCommand.Received.Unsigned"
+    enum="RemoteCommandReceivedStatus" expires_after="2021-03-01">
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting received device remote commands. The metric includes
+    unsigned device remote commands, both valid and invalid. Valid remote
+    command is counted by its type. Invalid remote command is counted by a
+    reason why it is considered invalid (e.g. unknown type or duplication).
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.DeviceRemoteCommandInvalidations"
     enum="EnterprisePolicyInvalidations" expires_after="2021-03-01">
   <owner>asumaneev@google.com</owner>
@@ -45747,6 +45797,56 @@
   </summary>
 </histogram>
 
+<histogram name="Enterprise.UserRemoteCommand.Executed"
+    enum="RemoteCommandExecutionStatus" expires_after="2021-03-01">
+<!-- Name completed by histogram_suffixes name="Enterprise.RemoteCommandType". -->
+
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting status of executed user remote commands. The metric
+    includes only signed user remote commands. Reports a final status of
+    finished remote command (e.g. success, failure or termination).
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.UserRemoteCommand.Executed.Unsigned"
+    enum="RemoteCommandExecutionStatus" expires_after="2021-03-01">
+<!-- Name completed by histogram_suffixes name="Enterprise.RemoteCommandType". -->
+
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting status of executed user remote commands. The metric
+    includes only unsigned user remote commands. Reports a final status of
+    finished remote command (e.g. success, failure or termination).
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.UserRemoteCommand.Received"
+    enum="RemoteCommandReceivedStatus" expires_after="2021-03-01">
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting received user remote commands. The metric includes
+    signed user remote commands, both valid and invalid. Valid remote command is
+    counted by its type. Invalid remote command is counted by a reason why it is
+    considered invalid (e.g. unknown type or duplication).
+  </summary>
+</histogram>
+
+<histogram name="Enterprise.UserRemoteCommand.Received.Unsigned"
+    enum="RemoteCommandReceivedStatus" expires_after="2021-03-01">
+  <owner>asumaneev@google.com</owner>
+  <owner>managed-platforms@google.com</owner>
+  <summary>
+    Events for counting received user remote commands. The metric includes
+    unsigned user remote commands, both valid and invalid. Valid remote command
+    is counted by its type. Invalid remote command is counted by a reason why it
+    is considered invalid (e.g. unknown type or duplication).
+  </summary>
+</histogram>
+
 <histogram name="Enterprise.UserRemoteCommandInvalidations"
     enum="EnterprisePolicyInvalidations" expires_after="2021-03-01">
   <owner>asumaneev@google.com</owner>
@@ -108520,6 +108620,13 @@
   <summary>Time spent on specific OOBE screen.</summary>
 </histogram>
 
+<histogram name="OOBE.StepCompletionTimeByExitReason" units="ms"
+    expires_after="2020-10-01">
+  <owner>raleksandrov@google.com</owner>
+  <owner>cros-oac@google.com</owner>
+  <summary>Time spent on specific OOBE screen grouped by exit reason.</summary>
+</histogram>
+
 <histogram name="OOBE.SyncConsentScreen.ReviewFollowingSetup"
     enum="BooleanChecked" expires_after="2020-10-01">
   <owner>raleksandrov@google.com</owner>
@@ -188124,6 +188231,29 @@
   </affected-histogram>
 </histogram_suffixes>
 
+<histogram_suffixes name="Enterprise.RemoteCommandType" separator=".">
+  <suffix name="CommandEchoTest" label="Command echo test"/>
+  <suffix name="DeviceFetchStatus" label="Fetch status"/>
+  <suffix name="DeviceGetAvailableDiagnosticRoutines"
+      label="Get available diagnostic routines"/>
+  <suffix name="DeviceGetDiagnosticRoutineUpdate"
+      label="Get diagnostic routine update"/>
+  <suffix name="DeviceReboot" label="Reboot"/>
+  <suffix name="DeviceRefreshEnterpriseMachineCertificate"
+      label="Refresh enterprise machine certificate"/>
+  <suffix name="DeviceRemotePowerwash" label="Powerwash"/>
+  <suffix name="DeviceRunDiagnosticRoutine" label="Run diagnostic routine"/>
+  <suffix name="DeviceScreenshot" label="Screenshot"/>
+  <suffix name="DeviceSetVolume" label="Set volume"/>
+  <suffix name="DeviceStartCrdSession" label="Device start CRD session"/>
+  <suffix name="DeviceWipeUsers" label="Wipe users"/>
+  <suffix name="UserArcCommand" label="ARC command"/>
+  <affected-histogram name="Enterprise.DeviceRemoteCommand.Executed"/>
+  <affected-histogram name="Enterprise.DeviceRemoteCommand.Executed.Unsigned"/>
+  <affected-histogram name="Enterprise.UserRemoteCommand.Executed"/>
+  <affected-histogram name="Enterprise.UserRemoteCommand.Executed.Unsigned"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="Enterprise.ResourceCacheTiming" separator=".">
   <obsolete>
     Removed 01/2020 since the histogram indicators were stable.
@@ -193730,6 +193860,52 @@
   <affected-histogram name="OOBE.StepCompletionTime"/>
 </histogram_suffixes>
 
+<histogram_suffixes name="OobeScreenName.ExitReason" separator=".">
+  <suffix name="Adb-sideloading.Next" label=""/>
+  <suffix name="App-downloading.Next" label=""/>
+  <suffix name="Arc-tos.Accepted" label=""/>
+  <suffix name="Arc-tos.Back" label=""/>
+  <suffix name="Arc-tos.Skipped" label=""/>
+  <suffix name="Assistant-optin-flow.Next" label=""/>
+  <suffix name="Auto-enrollment-check.Next" label=""/>
+  <suffix name="Autolaunch.Canceled" label=""/>
+  <suffix name="Autolaunch.Completed" label=""/>
+  <suffix name="Connect.Next" label=""/>
+  <suffix name="Debugging.Next" label=""/>
+  <suffix name="Demo-preferences.Canceled" label=""/>
+  <suffix name="Demo-preferences.Completed" label=""/>
+  <suffix name="Demo-setup.Completed" label=""/>
+  <suffix name="Device-disabled.Canceled" label=""/>
+  <suffix name="Discover.Next" label=""/>
+  <suffix name="Eula.AcceptedWithoutStats" label=""/>
+  <suffix name="Eula.AcceptedWithStats" label=""/>
+  <suffix name="Eula.Back" label=""/>
+  <suffix name="Fingerprint-setup.Next" label=""/>
+  <suffix name="Gesture-navigation.Next" label=""/>
+  <suffix name="Hid-detection.Next" label=""/>
+  <suffix name="Kiosk-enable.Next" label=""/>
+  <suffix name="Marketing-opt-in.Next" label=""/>
+  <suffix name="Multidevice-setup.Next" label=""/>
+  <suffix name="Network-selection.Back" label=""/>
+  <suffix name="Network-selection.Connected" label=""/>
+  <suffix name="Network-selection.OfflineDemoSetup" label=""/>
+  <suffix name="Oauth-enrollment.Back" label=""/>
+  <suffix name="Oauth-enrollment.Completed" label=""/>
+  <suffix name="Packaged-license.DontEnroll" label=""/>
+  <suffix name="Packaged-license.Enroll" label=""/>
+  <suffix name="Recommend-apps.Selected" label=""/>
+  <suffix name="Recommend-apps.Skipped" label=""/>
+  <suffix name="Reset.Cancel" label=""/>
+  <suffix name="Supervision-transition.Next" label=""/>
+  <suffix name="Sync-consent.Next" label=""/>
+  <suffix name="Terms-of-service.Accepted" label=""/>
+  <suffix name="Terms-of-service.Declined" label=""/>
+  <suffix name="Update.UpdateError" label=""/>
+  <suffix name="Update.UpdateNotRequired" label=""/>
+  <suffix name="Wrong-hwid.Next" label=""/>
+  <affected-histogram name="OOBE.StepCompletionTimeByExitReason"/>
+</histogram_suffixes>
+
 <histogram_suffixes name="OptimizationGuide.OptimizationTargets" separator=".">
   <suffix name="PainfulPageLoad" label="Painful page load"/>
   <affected-histogram name="OptimizationGuide.IsPredictionModelValid"/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml
index 3696af9c..c7f3afa 100644
--- a/tools/metrics/ukm/ukm.xml
+++ b/tools/metrics/ukm/ukm.xml
@@ -3701,6 +3701,13 @@
       make it easier to work with queries / dashboards for the overall
       CrossOriginFetchFromContentScript3 event.
     </summary>
+    <aggregation>
+      <history>
+        <statistics>
+          <enumeration/>
+        </statistics>
+      </history>
+    </aggregation>
   </metric>
 </event>
 
diff --git a/tools/perf/BUILD.gn b/tools/perf/BUILD.gn
index 7af7567..30cbd29 100644
--- a/tools/perf/BUILD.gn
+++ b/tools/perf/BUILD.gn
@@ -93,3 +93,9 @@
     data_deps += [ "//chrome/android/webapk/shell_apk:maps_go_webapk" ]
   }
 }
+
+# This group makes visible those targets in subdirectories that are not
+# explicitly depended on.
+group("gn_all") {
+  deps = [ "//tools/perf/core/perfetto_binary_roller:upload_trace_processor" ]
+}
diff --git a/tools/perf/benchmarks/rendering.py b/tools/perf/benchmarks/rendering.py
index 3f7a63f..726c425 100644
--- a/tools/perf/benchmarks/rendering.py
+++ b/tools/perf/benchmarks/rendering.py
@@ -38,6 +38,9 @@
     'Graphics.Smoothness.PercentDroppedFrames.CompositorThread.PinchZoom',
     'Graphics.Smoothness.PercentDroppedFrames.CompositorThread.TouchScroll',
     'Graphics.Smoothness.PercentDroppedFrames.CompositorThread.WheelScroll',
+    'Graphics.Smoothness.PercentDroppedFrames.MainThread.Universal',
+    'Graphics.Smoothness.PercentDroppedFrames.CompositorThread.Universal',
+    'Graphics.Smoothness.PercentDroppedFrames.SlowerThread.Universal',
     'Graphics.Smoothness.PercentDroppedFrames.ScrollingThread.TouchScroll',
     'Graphics.Smoothness.PercentDroppedFrames.ScrollingThread.WheelScroll',
     'Memory.GPU.PeakMemoryUsage.Scroll',
diff --git a/tools/perf/page_sets/rendering/throughput_test_cases.py b/tools/perf/page_sets/rendering/throughput_test_cases.py
index d8dde78d..c1d6c8c 100644
--- a/tools/perf/page_sets/rendering/throughput_test_cases.py
+++ b/tools/perf/page_sets/rendering/throughput_test_cases.py
@@ -56,6 +56,13 @@
          'main-impl-animations-throughput.html#60')
 
 
+class MainSixtyImplSixtyWithJankAndDelay(ThroughputMetricStory):
+  BASE_NAME = 'main_60fps_with_jank_and_delay_impl_60fps'
+  SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
+  URL = ('file://../../../../chrome/test/data/perf/throughput_test_cases/'
+         'main-impl-animations-throughput.html?jank&delay#60')
+
+
 class MainSixtyImplSixtyNoUpdate(ThroughputMetricStory):
   BASE_NAME = 'main_60fps_impl_60fps_no_update'
   SUPPORTED_PLATFORMS = platforms.ALL_PLATFORMS
diff --git a/ui/accessibility/ax_position.h b/ui/accessibility/ax_position.h
index 2af16128..a9cf694e 100644
--- a/ui/accessibility/ax_position.h
+++ b/ui/accessibility/ax_position.h
@@ -1093,11 +1093,13 @@
       return copy;
     }
 
-    // Blink doesn't always remove all deleted whitespace at the end of a
-    // textarea even though it will have adjusted its value attribute, because
-    // the extra layout objects are invisible. Therefore, we will stop at the
-    // last child that we can reach with the current text offset and ignore any
-    // remaining children.
+    // We stop at the last child that we can reach with the current text offset
+    // and ignore any remaining children. This is for defensive programming
+    // purposes, in case "MaxTextOffset" doesn't match the total length of all
+    // our children. This may happen if, for example, there is a bug in the
+    // internal accessibility tree we get from the renderer. In contrast, the
+    // current offset could not be greater than the length of all our children
+    // because the position would have been invalid.
     int current_offset = 0;
     int child_index = 0;
     for (; child_index < copy->AnchorChildCount(); ++child_index) {
@@ -1827,18 +1829,6 @@
 
         AXPositionInstance parent_position = CreateTextPosition(
             tree_id, parent_id, parent_offset, parent_affinity);
-        if (parent_position->IsNullPosition()) {
-          // Workaround: When the autofill feature populates a text field, it
-          // doesn't immediately update its value, which causes the text inside
-          // the user-agent shadow DOM to be different than the text in the text
-          // field itself. As a result, the parent_offset calculated above might
-          // appear to be temporarily invalid.
-          // TODO(nektar): Fix this better by ensuring that the text field's
-          // hypertext is always kept up to date.
-          parent_position =
-              CreateTextPosition(tree_id, parent_id, 0 /* text_offset */,
-                                 ax::mojom::TextAffinity::kDownstream);
-        }
 
         // If the current position is pointing at the end of its anchor, we need
         // to check if the parent position has introduced ambiguity as to
diff --git a/ui/aura/demo/demo_main.cc b/ui/aura/demo/demo_main.cc
index fe5050a..0b1c1ca 100644
--- a/ui/aura/demo/demo_main.cc
+++ b/ui/aura/demo/demo_main.cc
@@ -195,8 +195,7 @@
   display::Screen::SetScreenInstance(test_screen.get());
   std::unique_ptr<aura::WindowTreeHost> host(
       test_screen->CreateHostForPrimaryDisplay());
-  std::unique_ptr<DemoWindowParentingClient> window_parenting_client(
-      new DemoWindowParentingClient(host->window()));
+  DemoWindowParentingClient window_parenting_client(host->window());
   aura::test::TestFocusClient focus_client(host->window());
 
   // Create a hierarchy of test windows.
diff --git a/ui/aura/native_window_occlusion_tracker_win_interactive_test.cc b/ui/aura/native_window_occlusion_tracker_win_interactive_test.cc
index 467a505..197f196 100644
--- a/ui/aura/native_window_occlusion_tracker_win_interactive_test.cc
+++ b/ui/aura/native_window_occlusion_tracker_win_interactive_test.cc
@@ -118,9 +118,6 @@
         features::kCalculateNativeWinOcclusion);
 
     AuraTestBase::SetUp();
-
-    display::Screen::SetScreenInstance(test_screen());
-
   }
 
   void SetNativeWindowBounds(HWND hwnd, const gfx::Rect& bounds) {
diff --git a/ui/aura/test/aura_test_helper.cc b/ui/aura/test/aura_test_helper.cc
index 00c3dec9..f007733 100644
--- a/ui/aura/test/aura_test_helper.cc
+++ b/ui/aura/test/aura_test_helper.cc
@@ -5,11 +5,9 @@
 #include "ui/aura/test/aura_test_helper.h"
 
 #include "base/bind.h"
-#include "base/command_line.h"
 #include "base/run_loop.h"
 #include "build/build_config.h"
 #include "ui/aura/client/default_capture_client.h"
-#include "ui/aura/client/focus_client.h"
 #include "ui/aura/env.h"
 #include "ui/aura/input_state_lookup.h"
 #include "ui/aura/test/env_test_helper.h"
@@ -112,7 +110,6 @@
   display::Screen* screen = display::Screen::GetScreen();
   gfx::Size host_size(screen ? screen->GetPrimaryDisplay().GetSizeInPixel()
                              : gfx::Size(800, 600));
-
   test_screen_.reset(TestScreen::Create(host_size));
   // TODO(pkasting): Seems like we should either always set the screen instance,
   // or not create the screen/host if the test already has one; it doesn't make
@@ -202,13 +199,13 @@
 }
 
 Env* AuraTestHelper::GetEnv() {
-  return env_ ? env_.get() : Env::HasInstance() ? Env::GetInstance() : nullptr;
+  if (env_)
+    return env_.get();
+  return Env::HasInstance() ? Env::GetInstance() : nullptr;
 }
 
 ui::ContextFactory* AuraTestHelper::GetContextFactory() {
-  Env* env = GetEnv();
-  DCHECK(env);
-  return env->context_factory();
+  return GetEnv()->context_factory();
 }
 
 }  // namespace test
diff --git a/ui/aura/test/aura_test_helper.h b/ui/aura/test/aura_test_helper.h
index 4f66525..389b87d 100644
--- a/ui/aura/test/aura_test_helper.h
+++ b/ui/aura/test/aura_test_helper.h
@@ -30,6 +30,7 @@
 class FocusClient;
 class ScreenPositionClient;
 }
+
 namespace test {
 class TestWindowParentingClient;
 
diff --git a/ui/aura/test/test_focus_client.cc b/ui/aura/test/test_focus_client.cc
index afcf4dc..c76b2e5 100644
--- a/ui/aura/test/test_focus_client.cc
+++ b/ui/aura/test/test_focus_client.cc
@@ -14,9 +14,7 @@
 // TestFocusClient, public:
 
 TestFocusClient::TestFocusClient(Window* root_window)
-    : root_window_(root_window),
-      focused_window_(nullptr),
-      observer_manager_(this) {
+    : root_window_(root_window) {
   DCHECK(root_window_);
   client::SetFocusClient(root_window_, this);
 }
diff --git a/ui/aura/test/test_focus_client.h b/ui/aura/test/test_focus_client.h
index aa1435b..e7b1114 100644
--- a/ui/aura/test/test_focus_client.h
+++ b/ui/aura/test/test_focus_client.h
@@ -33,8 +33,8 @@
   void OnWindowDestroying(Window* window) override;
 
   Window* root_window_;
-  Window* focused_window_;
-  ScopedObserver<Window, WindowObserver> observer_manager_;
+  Window* focused_window_ = nullptr;
+  ScopedObserver<Window, WindowObserver> observer_manager_{this};
   base::ObserverList<aura::client::FocusChangeObserver>::Unchecked
       focus_observers_;
 
diff --git a/ui/base/x/x11_window.cc b/ui/base/x/x11_window.cc
index 6f5f225d..8fb12cb 100644
--- a/ui/base/x/x11_window.cc
+++ b/ui/base/x/x11_window.cc
@@ -1459,8 +1459,11 @@
   XSetWindowAttributes swa;
   swa.override_redirect = override_redirect;
   XChangeWindowAttributes(xdisplay_, xwindow_, CWOverrideRedirect, &swa);
-  if (remap)
+  if (remap) {
     Map();
+    if (has_pointer_grab_)
+      ui::ChangeActivePointerGrabCursor(x11::None);
+  }
 }
 
 bool XWindow::ContainsPointInRegion(const gfx::Point& point) const {
diff --git a/ui/gl/EGL/eglextchromium.h b/ui/gl/EGL/eglextchromium.h
index e66d34a..22e3cf0 100644
--- a/ui/gl/EGL/eglextchromium.h
+++ b/ui/gl/EGL/eglextchromium.h
@@ -22,21 +22,26 @@
 EGLAPI EGLBoolean EGLAPIENTRY eglGetSyncValuesCHROMIUM(
     EGLDisplay dpy, EGLSurface surface, EGLuint64CHROMIUM *ust,
     EGLuint64CHROMIUM *msc, EGLuint64CHROMIUM *sbc);
-EGLAPI EGLBoolean EGLAPIENTRY eglGetMscRateCHROMIUM(EGLDisplay dpy,
-                                                    EGLSurface surface,
-                                                    EGLint* numerator,
-                                                    EGLint* denominator);
 #endif /* EGL_EGLEXT_PROTOTYPES */
 typedef EGLBoolean (EGLAPIENTRYP PFNEGLGETSYNCVALUESCHROMIUMPROC)
     (EGLDisplay dpy, EGLSurface surface, EGLuint64CHROMIUM *ust,
      EGLuint64CHROMIUM *msc, EGLuint64CHROMIUM *sbc);
-typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETMSCRATECHROMIUMPROC)(
-    EGLDisplay dpy,
-    EGLSurface surface,
-    EGLint* numerator,
-    EGLint* denominator);
-#endif
-#endif
+#endif /* EGL_CHROMIUM_sync_control */
+
+#ifndef EGL_ANGLE_sync_control_rate
+#define EGL_ANGLE_sync_control_rate 1
+#ifdef EGL_EGLEXT_PROTOTYPES
+EGLAPI EGLBoolean EGLAPIENTRY eglGetMscRateANGLE(EGLDisplay dpy,
+                                                 EGLSurface surface,
+                                                 EGLint* numerator,
+                                                 EGLint* denominator);
+#endif /* EGL_EGLEXT_PROTOTYPES */
+typedef EGLBoolean(EGLAPIENTRYP PFNEGLGETMSCRATEANGLEPROC)(EGLDisplay dpy,
+                                                           EGLSurface surface,
+                                                           EGLint* numerator,
+                                                           EGLint* denominator);
+#endif /* EGL_ANGLE_sync_control_rate */
+#endif /* KHRONOS_SUPPORT_INT64 */
 
 #ifdef __cplusplus
 }
diff --git a/ui/gl/egl_bindings_autogen_mock.cc b/ui/gl/egl_bindings_autogen_mock.cc
index f6562ad..d4d3a04 100644
--- a/ui/gl/egl_bindings_autogen_mock.cc
+++ b/ui/gl/egl_bindings_autogen_mock.cc
@@ -299,12 +299,12 @@
 }
 
 EGLBoolean GL_BINDING_CALL
-MockEGLInterface::Mock_eglGetMscRateCHROMIUM(EGLDisplay dpy,
-                                             EGLSurface surface,
-                                             EGLint* numerator,
-                                             EGLint* denominator) {
-  MakeEglMockFunctionUnique("eglGetMscRateCHROMIUM");
-  return interface_->GetMscRateCHROMIUM(dpy, surface, numerator, denominator);
+MockEGLInterface::Mock_eglGetMscRateANGLE(EGLDisplay dpy,
+                                          EGLSurface surface,
+                                          EGLint* numerator,
+                                          EGLint* denominator) {
+  MakeEglMockFunctionUnique("eglGetMscRateANGLE");
+  return interface_->GetMscRateANGLE(dpy, surface, numerator, denominator);
 }
 
 EGLClientBuffer GL_BINDING_CALL
@@ -706,8 +706,8 @@
   if (strcmp(name, "eglGetFrameTimestampsANDROID") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_eglGetFrameTimestampsANDROID);
-  if (strcmp(name, "eglGetMscRateCHROMIUM") == 0)
-    return reinterpret_cast<GLFunctionPointerType>(Mock_eglGetMscRateCHROMIUM);
+  if (strcmp(name, "eglGetMscRateANGLE") == 0)
+    return reinterpret_cast<GLFunctionPointerType>(Mock_eglGetMscRateANGLE);
   if (strcmp(name, "eglGetNativeClientBufferANDROID") == 0)
     return reinterpret_cast<GLFunctionPointerType>(
         Mock_eglGetNativeClientBufferANDROID);
diff --git a/ui/gl/egl_bindings_autogen_mock.h b/ui/gl/egl_bindings_autogen_mock.h
index 06f1117..4b7eb01 100644
--- a/ui/gl/egl_bindings_autogen_mock.h
+++ b/ui/gl/egl_bindings_autogen_mock.h
@@ -131,11 +131,10 @@
                                   EGLint numTimestamps,
                                   EGLint* timestamps,
                                   EGLnsecsANDROID* values);
-static EGLBoolean GL_BINDING_CALL
-Mock_eglGetMscRateCHROMIUM(EGLDisplay dpy,
-                           EGLSurface surface,
-                           EGLint* numerator,
-                           EGLint* denominator);
+static EGLBoolean GL_BINDING_CALL Mock_eglGetMscRateANGLE(EGLDisplay dpy,
+                                                          EGLSurface surface,
+                                                          EGLint* numerator,
+                                                          EGLint* denominator);
 static EGLClientBuffer GL_BINDING_CALL Mock_eglGetNativeClientBufferANDROID(
     const struct AHardwareBuffer* ahardwarebuffer);
 static EGLBoolean GL_BINDING_CALL
diff --git a/ui/gl/generate_bindings.py b/ui/gl/generate_bindings.py
index 6a89047..01da79a 100755
--- a/ui/gl/generate_bindings.py
+++ b/ui/gl/generate_bindings.py
@@ -2452,9 +2452,9 @@
                  ] }],
   'arguments': 'EGLDisplay dpy, EGLSurface surface, EGLint timestamp', },
 { 'return_type': 'EGLBoolean',
-  'versions': [{ 'name': 'eglGetMscRateCHROMIUM',
+  'versions': [{ 'name': 'eglGetMscRateANGLE',
                  'extensions': [
-                   'EGL_CHROMIUM_sync_control'
+                   'EGL_ANGLE_sync_control_rate'
                  ] }],
   'arguments':
       'EGLDisplay dpy, EGLSurface surface, '
diff --git a/ui/gl/gl_bindings_api_autogen_egl.h b/ui/gl/gl_bindings_api_autogen_egl.h
index 8628d7cd..30b0ba80 100644
--- a/ui/gl/gl_bindings_api_autogen_egl.h
+++ b/ui/gl/gl_bindings_api_autogen_egl.h
@@ -111,10 +111,10 @@
 EGLBoolean eglGetFrameTimestampSupportedANDROIDFn(EGLDisplay dpy,
                                                   EGLSurface surface,
                                                   EGLint timestamp) override;
-EGLBoolean eglGetMscRateCHROMIUMFn(EGLDisplay dpy,
-                                   EGLSurface surface,
-                                   EGLint* numerator,
-                                   EGLint* denominator) override;
+EGLBoolean eglGetMscRateANGLEFn(EGLDisplay dpy,
+                                EGLSurface surface,
+                                EGLint* numerator,
+                                EGLint* denominator) override;
 EGLClientBuffer eglGetNativeClientBufferANDROIDFn(
     const struct AHardwareBuffer* ahardwarebuffer) override;
 EGLBoolean eglGetNextFrameIdANDROIDFn(EGLDisplay dpy,
diff --git a/ui/gl/gl_bindings_autogen_egl.cc b/ui/gl/gl_bindings_autogen_egl.cc
index 2f1ed115..18444223 100644
--- a/ui/gl/gl_bindings_autogen_egl.cc
+++ b/ui/gl/gl_bindings_autogen_egl.cc
@@ -187,6 +187,8 @@
       gfx::HasExtension(extensions, "EGL_ANGLE_stream_producer_d3d_texture");
   ext.b_EGL_ANGLE_surface_d3d_texture_2d_share_handle = gfx::HasExtension(
       extensions, "EGL_ANGLE_surface_d3d_texture_2d_share_handle");
+  ext.b_EGL_ANGLE_sync_control_rate =
+      gfx::HasExtension(extensions, "EGL_ANGLE_sync_control_rate");
   ext.b_EGL_CHROMIUM_sync_control =
       gfx::HasExtension(extensions, "EGL_CHROMIUM_sync_control");
   ext.b_EGL_EXT_image_flush_external =
@@ -278,9 +280,9 @@
             GetGLProcAddress("eglGetFrameTimestampSupportedANDROID"));
   }
 
-  if (ext.b_EGL_CHROMIUM_sync_control) {
-    fn.eglGetMscRateCHROMIUMFn = reinterpret_cast<eglGetMscRateCHROMIUMProc>(
-        GetGLProcAddress("eglGetMscRateCHROMIUM"));
+  if (ext.b_EGL_ANGLE_sync_control_rate) {
+    fn.eglGetMscRateANGLEFn = reinterpret_cast<eglGetMscRateANGLEProc>(
+        GetGLProcAddress("eglGetMscRateANGLE"));
   }
 
   if (ext.b_EGL_ANDROID_get_native_client_buffer) {
@@ -602,12 +604,11 @@
                                                             timestamp);
 }
 
-EGLBoolean EGLApiBase::eglGetMscRateCHROMIUMFn(EGLDisplay dpy,
-                                               EGLSurface surface,
-                                               EGLint* numerator,
-                                               EGLint* denominator) {
-  return driver_->fn.eglGetMscRateCHROMIUMFn(dpy, surface, numerator,
-                                             denominator);
+EGLBoolean EGLApiBase::eglGetMscRateANGLEFn(EGLDisplay dpy,
+                                            EGLSurface surface,
+                                            EGLint* numerator,
+                                            EGLint* denominator) {
+  return driver_->fn.eglGetMscRateANGLEFn(dpy, surface, numerator, denominator);
 }
 
 EGLClientBuffer EGLApiBase::eglGetNativeClientBufferANDROIDFn(
@@ -1117,13 +1118,12 @@
                                                           timestamp);
 }
 
-EGLBoolean TraceEGLApi::eglGetMscRateCHROMIUMFn(EGLDisplay dpy,
-                                                EGLSurface surface,
-                                                EGLint* numerator,
-                                                EGLint* denominator) {
-  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceEGLAPI::eglGetMscRateCHROMIUM")
-  return egl_api_->eglGetMscRateCHROMIUMFn(dpy, surface, numerator,
-                                           denominator);
+EGLBoolean TraceEGLApi::eglGetMscRateANGLEFn(EGLDisplay dpy,
+                                             EGLSurface surface,
+                                             EGLint* numerator,
+                                             EGLint* denominator) {
+  TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceEGLAPI::eglGetMscRateANGLE")
+  return egl_api_->eglGetMscRateANGLEFn(dpy, surface, numerator, denominator);
 }
 
 EGLClientBuffer TraceEGLApi::eglGetNativeClientBufferANDROIDFn(
@@ -1811,16 +1811,16 @@
   return result;
 }
 
-EGLBoolean LogEGLApi::eglGetMscRateCHROMIUMFn(EGLDisplay dpy,
-                                              EGLSurface surface,
-                                              EGLint* numerator,
-                                              EGLint* denominator) {
-  GL_SERVICE_LOG("eglGetMscRateCHROMIUM"
+EGLBoolean LogEGLApi::eglGetMscRateANGLEFn(EGLDisplay dpy,
+                                           EGLSurface surface,
+                                           EGLint* numerator,
+                                           EGLint* denominator) {
+  GL_SERVICE_LOG("eglGetMscRateANGLE"
                  << "(" << dpy << ", " << surface << ", "
                  << static_cast<const void*>(numerator) << ", "
                  << static_cast<const void*>(denominator) << ")");
   EGLBoolean result =
-      egl_api_->eglGetMscRateCHROMIUMFn(dpy, surface, numerator, denominator);
+      egl_api_->eglGetMscRateANGLEFn(dpy, surface, numerator, denominator);
   GL_SERVICE_LOG("GL_RESULT: " << result);
   return result;
 }
diff --git a/ui/gl/gl_bindings_autogen_egl.h b/ui/gl/gl_bindings_autogen_egl.h
index 546e2c9..6cafd146 100644
--- a/ui/gl/gl_bindings_autogen_egl.h
+++ b/ui/gl/gl_bindings_autogen_egl.h
@@ -141,7 +141,7 @@
     EGLDisplay dpy,
     EGLSurface surface,
     EGLint timestamp);
-typedef EGLBoolean(GL_BINDING_CALL* eglGetMscRateCHROMIUMProc)(
+typedef EGLBoolean(GL_BINDING_CALL* eglGetMscRateANGLEProc)(
     EGLDisplay dpy,
     EGLSurface surface,
     EGLint* numerator,
@@ -296,6 +296,7 @@
   bool b_EGL_ANGLE_query_surface_pointer;
   bool b_EGL_ANGLE_stream_producer_d3d_texture;
   bool b_EGL_ANGLE_surface_d3d_texture_2d_share_handle;
+  bool b_EGL_ANGLE_sync_control_rate;
   bool b_EGL_CHROMIUM_sync_control;
   bool b_EGL_EXT_image_flush_external;
   bool b_EGL_KHR_fence_sync;
@@ -351,7 +352,7 @@
   eglGetFrameTimestampsANDROIDProc eglGetFrameTimestampsANDROIDFn;
   eglGetFrameTimestampSupportedANDROIDProc
       eglGetFrameTimestampSupportedANDROIDFn;
-  eglGetMscRateCHROMIUMProc eglGetMscRateCHROMIUMFn;
+  eglGetMscRateANGLEProc eglGetMscRateANGLEFn;
   eglGetNativeClientBufferANDROIDProc eglGetNativeClientBufferANDROIDFn;
   eglGetNextFrameIdANDROIDProc eglGetNextFrameIdANDROIDFn;
   eglGetPlatformDisplayProc eglGetPlatformDisplayFn;
@@ -512,10 +513,10 @@
       EGLDisplay dpy,
       EGLSurface surface,
       EGLint timestamp) = 0;
-  virtual EGLBoolean eglGetMscRateCHROMIUMFn(EGLDisplay dpy,
-                                             EGLSurface surface,
-                                             EGLint* numerator,
-                                             EGLint* denominator) = 0;
+  virtual EGLBoolean eglGetMscRateANGLEFn(EGLDisplay dpy,
+                                          EGLSurface surface,
+                                          EGLint* numerator,
+                                          EGLint* denominator) = 0;
   virtual EGLClientBuffer eglGetNativeClientBufferANDROIDFn(
       const struct AHardwareBuffer* ahardwarebuffer) = 0;
   virtual EGLBoolean eglGetNextFrameIdANDROIDFn(EGLDisplay dpy,
@@ -685,8 +686,7 @@
   ::gl::g_current_egl_context->eglGetFrameTimestampsANDROIDFn
 #define eglGetFrameTimestampSupportedANDROID \
   ::gl::g_current_egl_context->eglGetFrameTimestampSupportedANDROIDFn
-#define eglGetMscRateCHROMIUM \
-  ::gl::g_current_egl_context->eglGetMscRateCHROMIUMFn
+#define eglGetMscRateANGLE ::gl::g_current_egl_context->eglGetMscRateANGLEFn
 #define eglGetNativeClientBufferANDROID \
   ::gl::g_current_egl_context->eglGetNativeClientBufferANDROIDFn
 #define eglGetNextFrameIdANDROID \
diff --git a/ui/gl/gl_enums_implementation_autogen.h b/ui/gl/gl_enums_implementation_autogen.h
index d35439c8..1209923b 100644
--- a/ui/gl/gl_enums_implementation_autogen.h
+++ b/ui/gl/gl_enums_implementation_autogen.h
@@ -829,10 +829,6 @@
         "GL_FILL_NV",
     },
     {
-        0x1D00,
-        "GL_FLAT_CHROMIUM",
-    },
-    {
         0x1E00,
         "GL_KEEP",
     },
@@ -877,14 +873,6 @@
         "GL_FONT_NUM_GLYPH_INDICES_BIT_NV",
     },
     {
-        0x2400,
-        "GL_EYE_LINEAR_CHROMIUM",
-    },
-    {
-        0x2401,
-        "GL_OBJECT_LINEAR_CHROMIUM",
-    },
-    {
         0x2600,
         "GL_NEAREST",
     },
@@ -2053,10 +2041,6 @@
         "GL_MAX_CUBE_MAP_TEXTURE_SIZE",
     },
     {
-        0x8576,
-        "GL_CONSTANT_CHROMIUM",
-    },
-    {
         0x8589,
         "GL_SRC1_ALPHA_EXT",
     },
@@ -4629,10 +4613,6 @@
         "GL_PATH_CLIENT_LENGTH_NV",
     },
     {
-        0x907a,
-        "GL_PATH_MITER_LIMIT_CHROMIUM",
-    },
-    {
         0x9080,
         "GL_PATH_FILL_MODE_NV",
     },
@@ -4653,10 +4633,6 @@
         "GL_PATH_STROKE_MASK_NV",
     },
     {
-        0x9086,
-        "GL_PATH_STROKE_BOUND_CHROMIUM",
-    },
-    {
         0x9088,
         "GL_COUNT_UP_NV",
     },
@@ -4965,14 +4941,6 @@
         "GL_CONTEXT_ROBUST_ACCESS_KHR",
     },
     {
-        0x90a3,
-        "GL_SQUARE_CHROMIUM",
-    },
-    {
-        0x90a4,
-        "GL_ROUND_CHROMIUM",
-    },
-    {
         0x9100,
         "GL_TEXTURE_2D_MULTISAMPLE",
     },
diff --git a/ui/gl/gl_mock_autogen_egl.h b/ui/gl/gl_mock_autogen_egl.h
index bcdd2a3a..ba2f95f 100644
--- a/ui/gl/gl_mock_autogen_egl.h
+++ b/ui/gl/gl_mock_autogen_egl.h
@@ -121,7 +121,7 @@
                         EGLnsecsANDROID* values));
 MOCK_METHOD3(GetFrameTimestampSupportedANDROID,
              EGLBoolean(EGLDisplay dpy, EGLSurface surface, EGLint timestamp));
-MOCK_METHOD4(GetMscRateCHROMIUM,
+MOCK_METHOD4(GetMscRateANGLE,
              EGLBoolean(EGLDisplay dpy,
                         EGLSurface surface,
                         EGLint* numerator,
diff --git a/ui/gl/gl_surface_egl.cc b/ui/gl/gl_surface_egl.cc
index 72268904..750f3ede2 100644
--- a/ui/gl/gl_surface_egl.cc
+++ b/ui/gl/gl_surface_egl.cc
@@ -174,6 +174,7 @@
 bool g_egl_create_context_bind_generates_resource_supported = false;
 bool g_egl_create_context_webgl_compatability_supported = false;
 bool g_egl_sync_control_supported = false;
+bool g_egl_sync_control_rate_supported = false;
 bool g_egl_window_fixed_size_supported = false;
 bool g_egl_surfaceless_context_supported = false;
 bool g_egl_surface_orientation_supported = false;
@@ -266,14 +267,12 @@
   }
 
   bool GetMscRate(int32_t* numerator, int32_t* denominator) override {
-    // TODO(https://crbug.com/1064078): eglGetMscRateCHROMIUM is not universally
-    // available when the EGL_CHROMIUM_sync_control extension is present (for
-    // example, EGL Mesa).
-    if (!gl::g_driver_egl.fn.eglGetMscRateCHROMIUMFn)
+    if (!g_egl_sync_control_rate_supported) {
       return false;
+    }
 
-    bool result = eglGetMscRateCHROMIUM(g_egl_display, surface_, numerator,
-                                        denominator) == EGL_TRUE;
+    bool result = eglGetMscRateANGLE(g_egl_display, surface_, numerator,
+                                     denominator) == EGL_TRUE;
     return result;
   }
 
@@ -921,6 +920,8 @@
   g_egl_create_context_webgl_compatability_supported =
       HasEGLExtension("EGL_ANGLE_create_context_webgl_compatibility");
   g_egl_sync_control_supported = HasEGLExtension("EGL_CHROMIUM_sync_control");
+  g_egl_sync_control_rate_supported =
+      HasEGLExtension("EGL_ANGLE_sync_control_rate");
   g_egl_window_fixed_size_supported =
       HasEGLExtension("EGL_ANGLE_window_fixed_size");
   g_egl_surface_orientation_supported =
@@ -1036,6 +1037,7 @@
   g_egl_create_context_bind_generates_resource_supported = false;
   g_egl_create_context_webgl_compatability_supported = false;
   g_egl_sync_control_supported = false;
+  g_egl_sync_control_rate_supported = false;
   g_egl_window_fixed_size_supported = false;
   g_egl_surface_orientation_supported = false;
   g_egl_surfaceless_context_supported = false;
diff --git a/ui/message_center/views/notification_header_view_unittest.cc b/ui/message_center/views/notification_header_view_unittest.cc
index feef9b1..26520d1 100644
--- a/ui/message_center/views/notification_header_view_unittest.cc
+++ b/ui/message_center/views/notification_header_view_unittest.cc
@@ -66,22 +66,22 @@
                 IDS_MESSAGE_NOTIFICATION_DURATION_HOURS_SHORTEST_FUTURE, 3),
             timestamp_view->GetText());
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromHours(3));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromHours(3));
+  task_environment()->RunUntilIdle();
 
   EXPECT_EQ(l10n_util::GetPluralStringFUTF16(
                 IDS_MESSAGE_NOTIFICATION_DURATION_MINUTES_SHORTEST_FUTURE, 30),
             timestamp_view->GetText());
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromMinutes(30));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(30));
+  task_environment()->RunUntilIdle();
 
   EXPECT_EQ(
       l10n_util::GetStringUTF16(IDS_MESSAGE_NOTIFICATION_NOW_STRING_SHORTEST),
       timestamp_view->GetText());
 
-  task_environment_->FastForwardBy(base::TimeDelta::FromDays(2));
-  task_environment_->RunUntilIdle();
+  task_environment()->FastForwardBy(base::TimeDelta::FromDays(2));
+  task_environment()->RunUntilIdle();
 
   EXPECT_EQ(l10n_util::GetPluralStringFUTF16(
                 IDS_MESSAGE_NOTIFICATION_DURATION_DAYS_SHORTEST, 2),
diff --git a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
index 9152402..e8b7646 100644
--- a/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
+++ b/ui/views/accessibility/view_ax_platform_node_delegate_unittest.cc
@@ -98,30 +98,25 @@
 
   // Sets up a more complicated structure of Views - one parent View with four
   // child Views.
-  std::vector<View*> SetUpExtraViews() {
-    View* parent_view = new View();
-    widget_->GetContentsView()->AddChildView(parent_view);
-    std::vector<View*> views{parent_view};
-
-    const int num_children = 4;
-    for (int i = 0; i < num_children; i++) {
-      View* child_view = new View();
-      parent_view->AddChildView(child_view);
-      views.push_back(child_view);
-    }
+  View::Views SetUpExtraViews() {
+    View* parent_view =
+        widget_->GetContentsView()->AddChildView(std::make_unique<View>());
+    View::Views views{parent_view};
+    for (int i = 0; i < 4; i++)
+      views.push_back(parent_view->AddChildView(std::make_unique<View>()));
     return views;
   }
 
   // Adds group id information to the first 5 values in |views|. If |views| is
   // empty, populates it with one parent View and four child Views. It is
   // assumed |views| is either empty or has at least 5 items.
-  void SetUpExtraViewsWithGroups(std::vector<View*>& views) {
+  void SetUpExtraViewsWithGroups(View::Views& views) {
     //                v[0] g1
     //     |        |        |      |
     // v[1] g1  v[2] g1  v[3] g2  v[4]
     if (views.empty())
       views = SetUpExtraViews();
-    EXPECT_GE(views.size(), (size_t)5);
+    ASSERT_GE(views.size(), 5u);
 
     views[0]->SetGroup(1);
     views[1]->SetGroup(1);
@@ -133,13 +128,13 @@
   // Adds posInSet and setSize overrides to the first 5 values in |views|. If
   // |views| is empty, populates it with one parent View and four child Views.
   // It is assumed |views| is either empty or has at least 5 items.
-  void SetUpExtraViewsWithSetOverrides(std::vector<View*>& views) {
+  void SetUpExtraViewsWithSetOverrides(View::Views& views) {
     //                     v[0] p4 s4
     //      |            |            |            |
     //  v[1] p3 s4   v[2] p2 s4   v[3] p- s-   v[4] p1 s4
     if (views.empty())
       views = SetUpExtraViews();
-    EXPECT_GE(views.size(), (size_t)5);
+    ASSERT_GE(views.size(), 5u);
 
     views[0]->GetViewAccessibility().OverridePosInSet(4, 4);
     views[1]->GetViewAccessibility().OverridePosInSet(3, 4);
@@ -246,7 +241,7 @@
 }
 
 TEST_F(ViewAXPlatformNodeDelegateTest, IsOrderedSet) {
-  std::vector<View*> group_ids;
+  View::Views group_ids;
   SetUpExtraViewsWithGroups(group_ids);
   // Only last element has no group id.
   EXPECT_TRUE(view_accessibility(group_ids[0])->IsOrderedSet());
@@ -261,7 +256,7 @@
   EXPECT_TRUE(view_accessibility(group_ids[3])->IsOrderedSetItem());
   EXPECT_FALSE(view_accessibility(group_ids[4])->IsOrderedSetItem());
 
-  std::vector<View*> overrides;
+  View::Views overrides;
   SetUpExtraViewsWithSetOverrides(overrides);
   // Only overrides[3] has no override values for setSize/ posInSet.
   EXPECT_TRUE(view_accessibility(overrides[0])->IsOrderedSet());
@@ -279,7 +274,7 @@
 
 TEST_F(ViewAXPlatformNodeDelegateTest, SetSizeAndPosition) {
   // Test Views with group ids.
-  std::vector<View*> group_ids;
+  View::Views group_ids;
   SetUpExtraViewsWithGroups(group_ids);
   EXPECT_EQ(view_accessibility(group_ids[0])->GetSetSize(), 3);
   EXPECT_EQ(view_accessibility(group_ids[0])->GetPosInSet(), 1);
@@ -307,7 +302,7 @@
   group_ids[2]->GetViewAccessibility().OverrideIsIgnored(false);
 
   // Test Views with setSize/ posInSet override values set.
-  std::vector<View*> overrides;
+  View::Views overrides;
   SetUpExtraViewsWithSetOverrides(overrides);
   EXPECT_EQ(view_accessibility(overrides[0])->GetSetSize(), 4);
   EXPECT_EQ(view_accessibility(overrides[0])->GetPosInSet(), 4);
@@ -341,7 +336,7 @@
 }
 
 TEST_F(ViewAXPlatformNodeDelegateTest, Navigation) {
-  std::vector<View*> view_ids = SetUpExtraViews();
+  View::Views view_ids = SetUpExtraViews();
 
   EXPECT_EQ(view_accessibility(view_ids[0])->GetNextSibling(), nullptr);
   EXPECT_EQ(view_accessibility(view_ids[0])->GetPreviousSibling(),
@@ -372,7 +367,7 @@
 }
 
 TEST_F(ViewAXPlatformNodeDelegateTest, OverrideHasPopup) {
-  std::vector<View*> view_ids = SetUpExtraViews();
+  View::Views view_ids = SetUpExtraViews();
 
   view_ids[1]->GetViewAccessibility().OverrideHasPopup(
       ax::mojom::HasPopup::kTrue);
diff --git a/ui/views/controls/textfield/textfield_unittest.cc b/ui/views/controls/textfield/textfield_unittest.cc
index a505c3c..e7ea20f 100644
--- a/ui/views/controls/textfield/textfield_unittest.cc
+++ b/ui/views/controls/textfield/textfield_unittest.cc
@@ -24,7 +24,6 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/scoped_clipboard_writer.h"
-#include "ui/base/clipboard/test/test_clipboard.h"
 #include "ui/base/dragdrop/drag_drop_types.h"
 #include "ui/base/emoji/emoji_panel_helper.h"
 #include "ui/base/ime/constants.h"
@@ -413,13 +412,6 @@
     ViewsTestBase::TearDown();
   }
 
-  void SetUp() override {
-    // OS clipboard is a global resource, which causes flakiness when unit tests
-    // run in parallel. So, use a per-instance test clipboard.
-    ui::TestClipboard::CreateForCurrentThread();
-    ViewsTestBase::SetUp();
-  }
-
   ui::ClipboardBuffer GetAndResetCopiedToClipboard() {
     ui::ClipboardBuffer clipboard_buffer = copied_to_clipboard_;
     copied_to_clipboard_ = ui::ClipboardBuffer::kMaxValue;
diff --git a/ui/views/controls/webview/web_dialog_view_unittest.cc b/ui/views/controls/webview/web_dialog_view_unittest.cc
index e3d1803..e19f2b7 100644
--- a/ui/views/controls/webview/web_dialog_view_unittest.cc
+++ b/ui/views/controls/webview/web_dialog_view_unittest.cc
@@ -50,11 +50,9 @@
 // Provides functionality to test a WebDialogView.
 class WebDialogViewUnitTest : public views::test::WidgetTest {
  public:
-  template <typename... TaskEnvironmentTraits>
-  NOINLINE explicit WebDialogViewUnitTest(TaskEnvironmentTraits&&... traits)
-      : views::test::WidgetTest(
-            views::test::WidgetTest::SubclassManagesTaskEnvironment()),
-        task_environment_(std::forward<TaskEnvironmentTraits>(traits)...) {}
+  WebDialogViewUnitTest()
+      : views::test::WidgetTest(std::unique_ptr<base::test::TaskEnvironment>(
+            std::make_unique<content::BrowserTaskEnvironment>())) {}
   ~WebDialogViewUnitTest() override = default;
 
   // testing::Test
@@ -127,9 +125,6 @@
       widget_->OnKeyEvent(&event_copy);
   }
 
-  // TaskEnvironment must be created first
-  content::BrowserTaskEnvironment task_environment_;
-
  private:
   content::TestContentBrowserClient test_browser_client_;
   std::unique_ptr<content::TestBrowserContext> browser_context_;
diff --git a/ui/views/controls/webview/webview_unittest.cc b/ui/views/controls/webview/webview_unittest.cc
index d6a58da5..5bfd431 100644
--- a/ui/views/controls/webview/webview_unittest.cc
+++ b/ui/views/controls/webview/webview_unittest.cc
@@ -25,7 +25,6 @@
 #include "ui/events/event.h"
 #include "ui/events/event_utils.h"
 #include "ui/views/controls/native/native_view_host.h"
-#include "ui/views/test/test_views_delegate.h"
 #include "ui/views/test/widget_test.h"
 
 #if defined(USE_AURA)
@@ -128,8 +127,9 @@
 class WebViewUnitTest : public views::test::WidgetTest {
  public:
   WebViewUnitTest()
-      : views::test::WidgetTest(
-            views::ViewsTestBase::SubclassManagesTaskEnvironment()) {}
+      : views::test::WidgetTest(std::unique_ptr<base::test::TaskEnvironment>(
+            std::make_unique<content::BrowserTaskEnvironment>())) {}
+
   ~WebViewUnitTest() override = default;
 
   std::unique_ptr<content::WebContents> CreateWebContentsForWebView(
@@ -146,7 +146,6 @@
     scoped_web_contents_creator_ =
         std::make_unique<views::WebView::ScopedWebContentsCreatorForTesting>(
             creator);
-    set_views_delegate(base::WrapUnique(new views::TestViewsDelegate));
     browser_context_ = std::make_unique<content::TestBrowserContext>();
     WidgetTest::SetUp();
     // Set the test content browser client to avoid pulling in needless
@@ -195,8 +194,6 @@
     web_view->fullscreen_native_view_for_testing_ = native_view;
   }
 
-  content::BrowserTaskEnvironment task_environment_;
-
  private:
   std::unique_ptr<content::RenderViewHostTestEnabler> rvh_enabler_;
   std::unique_ptr<content::TestBrowserContext> browser_context_;
diff --git a/ui/views/corewm/tooltip_controller_unittest.cc b/ui/views/corewm/tooltip_controller_unittest.cc
index 49debd7d..eedc4880 100644
--- a/ui/views/corewm/tooltip_controller_unittest.cc
+++ b/ui/views/corewm/tooltip_controller_unittest.cc
@@ -12,9 +12,7 @@
 #include "ui/accessibility/ax_node_data.h"
 #include "ui/aura/client/cursor_client.h"
 #include "ui/aura/client/window_types.h"
-#include "ui/aura/env.h"
 #include "ui/aura/test/aura_test_base.h"
-#include "ui/aura/test/test_screen.h"
 #include "ui/aura/test/test_window_delegate.h"
 #include "ui/aura/window.h"
 #include "ui/aura/window_event_dispatcher.h"
@@ -42,7 +40,6 @@
 
 #if BUILDFLAG(ENABLE_DESKTOP_AURA)
 #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h"
-#include "ui/views/widget/desktop_aura/desktop_screen.h"
 #endif
 
 using base::ASCIIToUTF16;
diff --git a/ui/views/test/scoped_views_test_helper.cc b/ui/views/test/scoped_views_test_helper.cc
index f8255f5..f747338 100644
--- a/ui/views/test/scoped_views_test_helper.cc
+++ b/ui/views/test/scoped_views_test_helper.cc
@@ -8,7 +8,6 @@
 
 #include "ui/base/clipboard/clipboard.h"
 #include "ui/base/clipboard/test/test_clipboard.h"
-#include "ui/base/ime/init/input_method_initializer.h"
 #include "ui/views/test/test_views_delegate.h"
 
 #if defined(USE_AURA)
@@ -27,13 +26,13 @@
                                        std::move(factory));
   test_helper_->SetUp();
 
-  ui::InitializeInputMethodForTesting();
+  // OS clipboard is a global resource, which causes flakiness when unit tests
+  // run in parallel. So, use a per-instance test clipboard.
   ui::TestClipboard::CreateForCurrentThread();
 }
 
 ScopedViewsTestHelper::~ScopedViewsTestHelper() {
   ui::Clipboard::DestroyClipboardForCurrentThread();
-  ui::ShutdownInputMethodForTesting();
 }
 
 gfx::NativeWindow ScopedViewsTestHelper::GetContext() {
diff --git a/ui/views/test/scoped_views_test_helper.h b/ui/views/test/scoped_views_test_helper.h
index 453e44e..45e7516a 100644
--- a/ui/views/test/scoped_views_test_helper.h
+++ b/ui/views/test/scoped_views_test_helper.h
@@ -29,6 +29,8 @@
       std::unique_ptr<TestViewsDelegate> test_views_delegate = nullptr,
       base::Optional<ViewsDelegate::NativeWidgetFactory> factory =
           base::nullopt);
+  ScopedViewsTestHelper(const ScopedViewsTestHelper&) = delete;
+  ScopedViewsTestHelper& operator=(const ScopedViewsTestHelper&) = delete;
   ~ScopedViewsTestHelper();
 
   // Returns the context for creating new windows. In Aura builds, this will be
@@ -45,8 +47,6 @@
  private:
   std::unique_ptr<ViewsTestHelper> test_helper_ = ViewsTestHelper::Create();
   std::unique_ptr<TestViewsDelegate> test_views_delegate_;
-
-  DISALLOW_COPY_AND_ASSIGN(ScopedViewsTestHelper);
 };
 
 }  // namespace views
diff --git a/ui/views/test/views_test_base.cc b/ui/views/test/views_test_base.cc
index f52b1a4..016f196 100644
--- a/ui/views/test/views_test_base.cc
+++ b/ui/views/test/views_test_base.cc
@@ -64,7 +64,8 @@
 }  // namespace
 
 ViewsTestBase::ViewsTestBase(
-    ViewsTestBase::SubclassManagesTaskEnvironment tag) {}
+    std::unique_ptr<base::test::TaskEnvironment> task_environment)
+    : task_environment_(std::move(task_environment)) {}
 
 ViewsTestBase::~ViewsTestBase() {
   CHECK(setup_called_)
diff --git a/ui/views/test/views_test_base.h b/ui/views/test/views_test_base.h
index dac4ce3..e2acd16 100644
--- a/ui/views/test/views_test_base.h
+++ b/ui/views/test/views_test_base.h
@@ -47,16 +47,13 @@
   // specified.
   template <typename... TaskEnvironmentTraits>
   NOINLINE explicit ViewsTestBase(TaskEnvironmentTraits&&... traits)
-      : task_environment_(base::in_place,
-                          base::test::TaskEnvironment::MainThreadType::UI,
-                          std::forward<TaskEnvironmentTraits>(traits)...) {
-  }
+      : ViewsTestBase(std::make_unique<base::test::TaskEnvironment>(
+            base::test::TaskEnvironment::MainThreadType::UI,
+            std::forward<TaskEnvironmentTraits>(traits)...)) {}
 
-  // Alternatively a subclass may pass this tag to ask this ViewsTestBase not to
-  // instantiate a TaskEnvironment. The subclass is then responsible to
-  // instantiate one before ViewsTestBase::SetUp().
-  struct SubclassManagesTaskEnvironment {};
-  explicit ViewsTestBase(SubclassManagesTaskEnvironment tag);
+  // Alternatively a subclass may pass a TaskEnvironment directly.
+  explicit ViewsTestBase(
+      std::unique_ptr<base::test::TaskEnvironment> task_environment);
 
   ~ViewsTestBase() override;
 
@@ -85,6 +82,9 @@
   void SimulateNativeDestroy(Widget* widget);
 
  protected:
+  base::test::TaskEnvironment* task_environment() {
+    return task_environment_.get();
+  }
   TestViewsDelegate* test_views_delegate() const {
     return test_helper_->test_views_delegate();
   }
@@ -121,16 +121,13 @@
       const Widget::InitParams& init_params,
       internal::NativeWidgetDelegate* delegate);
 
- protected:
   Widget::InitParams CreateParamsForTestWidget(
       Widget::InitParams::Type type =
           Widget::InitParams::TYPE_WINDOW_FRAMELESS);
 
-  // Initialized first, destroyed last. Use this protected member directly from
-  // the test body to drive tasks posted within a ViewsTestBase-based test.
-  base::Optional<base::test::TaskEnvironment> task_environment_;
-
  private:
+  std::unique_ptr<base::test::TaskEnvironment> task_environment_;
+
   // Controls what type of widget will be created by default for a test (i.e.
   // when creating a Widget and leaving InitParams::native_widget unspecified).
   // kDefault will allow the system default to be used (typically
diff --git a/ui/views/test/views_test_helper.h b/ui/views/test/views_test_helper.h
index 5ec856d..7d29d359 100644
--- a/ui/views/test/views_test_helper.h
+++ b/ui/views/test/views_test_helper.h
@@ -23,6 +23,8 @@
   // Create a platform specific instance.
   static std::unique_ptr<ViewsTestHelper> Create();
 
+  ViewsTestHelper(const ViewsTestHelper&) = delete;
+  ViewsTestHelper& operator=(const ViewsTestHelper&) = delete;
   virtual ~ViewsTestHelper() = default;
 
   // Returns the delegate to use if the test/owner does not create one.
@@ -36,15 +38,11 @@
   // Does any additional necessary setup of this object or its members.
   virtual void SetUp();
 
-  // Returns a context view. In aura builds, this will be the RootWindow.
-  // Everywhere else, null.
+  // Returns a context window, e.g. the Aura root window.
   virtual gfx::NativeWindow GetContext();
 
  protected:
   ViewsTestHelper() = default;
-
- private:
-  DISALLOW_COPY_AND_ASSIGN(ViewsTestHelper);
 };
 
 }  // namespace views
diff --git a/ui/views/test/views_test_helper_aura.h b/ui/views/test/views_test_helper_aura.h
index 5fdb4af..d003367 100644
--- a/ui/views/test/views_test_helper_aura.h
+++ b/ui/views/test/views_test_helper_aura.h
@@ -8,7 +8,6 @@
 #include <memory>
 
 #include "base/callback_forward.h"
-#include "base/macros.h"
 #include "ui/aura/test/aura_test_helper.h"
 #include "ui/views/test/views_test_helper.h"
 
@@ -20,6 +19,8 @@
       base::OnceCallback<std::unique_ptr<TestViewsDelegate>()>;
 
   ViewsTestHelperAura();
+  ViewsTestHelperAura(const ViewsTestHelperAura&) = delete;
+  ViewsTestHelperAura& operator=(const ViewsTestHelperAura&) = delete;
   ~ViewsTestHelperAura() override;
 
   // ViewsTestHelper:
@@ -36,8 +37,6 @@
  private:
   std::unique_ptr<aura::test::AuraTestHelper> aura_test_helper_;
   TestViewsDelegateFactory factory_;
-
-  DISALLOW_COPY_AND_ASSIGN(ViewsTestHelperAura);
 };
 
 }  // namespace views
diff --git a/ui/views/test/views_test_helper_mac.h b/ui/views/test/views_test_helper_mac.h
index 4e2d769..26fda78f 100644
--- a/ui/views/test/views_test_helper_mac.h
+++ b/ui/views/test/views_test_helper_mac.h
@@ -7,7 +7,6 @@
 
 #include <memory>
 
-#include "base/macros.h"
 #include "ui/base/test/scoped_fake_full_keyboard_access.h"
 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
 #include "ui/compositor/test/test_context_factories.h"
@@ -25,6 +24,8 @@
 class ViewsTestHelperMac : public ViewsTestHelper {
  public:
   ViewsTestHelperMac();
+  ViewsTestHelperMac(const ViewsTestHelperMac&) = delete;
+  ViewsTestHelperMac& operator=(const ViewsTestHelperMac&) = delete;
   ~ViewsTestHelperMac() override;
 
   // ViewsTestHelper:
@@ -57,8 +58,6 @@
   // more consistent with other platforms, where most views are focusable by
   // default.
   ui::test::ScopedFakeFullKeyboardAccess faked_full_keyboard_access_;
-
-  DISALLOW_COPY_AND_ASSIGN(ViewsTestHelperMac);
 };
 
 }  // namespace views
diff --git a/ui/views/test/widget_test.cc b/ui/views/test/widget_test.cc
index 296b508..f9fea984 100644
--- a/ui/views/test/widget_test.cc
+++ b/ui/views/test/widget_test.cc
@@ -16,6 +16,12 @@
   widget->CloseNow();
 }
 
+WidgetTest::WidgetTest() = default;
+
+WidgetTest::WidgetTest(
+    std::unique_ptr<base::test::TaskEnvironment> task_environment)
+    : ViewsTestBase(std::move(task_environment)) {}
+
 WidgetTest::~WidgetTest() = default;
 
 Widget* WidgetTest::CreateTopLevelPlatformWidget() {
diff --git a/ui/views/test/widget_test.h b/ui/views/test/widget_test.h
index c7de45c..fe2472c 100644
--- a/ui/views/test/widget_test.h
+++ b/ui/views/test/widget_test.h
@@ -46,14 +46,9 @@
 
   using WidgetAutoclosePtr = std::unique_ptr<Widget, WidgetCloser>;
 
-  // Constructs an AshTestBase with |traits| being forwarded to its
-  // TaskEnvironment. |ViewsTestBase::SubclassManagesTaskEnvironment()|
-  // can also be passed as a sole trait to indicate that this WidgetTest's
-  // subclass will manage the task environment.
-  template <typename... TaskEnvironmentTraits>
-  NOINLINE explicit WidgetTest(TaskEnvironmentTraits&&... traits)
-      : ViewsTestBase(std::forward<TaskEnvironmentTraits>(traits)...) {}
-
+  WidgetTest();
+  explicit WidgetTest(
+      std::unique_ptr<base::test::TaskEnvironment> task_environment);
   ~WidgetTest() override;
 
   // Create Widgets with |native_widget| in InitParams set to an instance of
diff --git a/ui/views_content_client/views_content_client_main_parts_chromeos.cc b/ui/views_content_client/views_content_client_main_parts_chromeos.cc
index cc498e94..c1cdf28 100644
--- a/ui/views_content_client/views_content_client_main_parts_chromeos.cc
+++ b/ui/views_content_client/views_content_client_main_parts_chromeos.cc
@@ -5,9 +5,7 @@
 #include "base/macros.h"
 #include "content/public/browser/context_factory.h"
 #include "content/shell/browser/shell_browser_context.h"
-#include "ui/aura/test/test_screen.h"
 #include "ui/aura/window.h"
-#include "ui/display/screen.h"
 #include "ui/views_content_client/views_content_client.h"
 #include "ui/views_content_client/views_content_client_main_parts_aura.h"
 #include "ui/wm/test/wm_test_helper.h"
@@ -30,7 +28,6 @@
 
  private:
   // Enable a minimal set of views::corewm to be initialized.
-  std::unique_ptr<display::Screen> test_screen_;
   std::unique_ptr<::wm::WMTestHelper> wm_test_helper_;
 
   DISALLOW_COPY_AND_ASSIGN(ViewsContentClientMainPartsChromeOS);
@@ -45,11 +42,8 @@
 void ViewsContentClientMainPartsChromeOS::PreMainMessageLoopRun() {
   ViewsContentClientMainPartsAura::PreMainMessageLoopRun();
 
-  gfx::Size host_size(800, 600);
-  test_screen_.reset(aura::TestScreen::Create(host_size));
-  display::Screen::SetScreenInstance(test_screen_.get());
   // Set up basic pieces of views::corewm.
-  wm_test_helper_ = std::make_unique<wm::WMTestHelper>(host_size);
+  wm_test_helper_ = std::make_unique<wm::WMTestHelper>(gfx::Size(800, 600));
   // Ensure the X window gets mapped.
   wm_test_helper_->host()->Show();
 
@@ -61,7 +55,6 @@
 
 void ViewsContentClientMainPartsChromeOS::PostMainMessageLoopRun() {
   wm_test_helper_.reset();
-  test_screen_.reset();
 
   ViewsContentClientMainPartsAura::PostMainMessageLoopRun();
 }
diff --git a/ui/wm/core/shadow_controller_unittest.cc b/ui/wm/core/shadow_controller_unittest.cc
index 4ca5ca6e..ee4fd2e 100644
--- a/ui/wm/core/shadow_controller_unittest.cc
+++ b/ui/wm/core/shadow_controller_unittest.cc
@@ -48,9 +48,8 @@
 
   void InstallShadowController(
       std::unique_ptr<ShadowControllerDelegate> delegate) {
-    ActivationClient* activation_client = GetActivationClient(root_window());
     shadow_controller_ = std::make_unique<ShadowController>(
-        activation_client, std::move(delegate));
+        GetActivationClient(root_window()), std::move(delegate));
   }
 
  private:
diff --git a/weblayer/browser/browser_main_parts_impl.cc b/weblayer/browser/browser_main_parts_impl.cc
index 5b01156..f0b6bcd 100644
--- a/weblayer/browser/browser_main_parts_impl.cc
+++ b/weblayer/browser/browser_main_parts_impl.cc
@@ -22,6 +22,7 @@
 #include "services/service_manager/embedder/result_codes.h"
 #include "ui/base/resource/resource_bundle.h"
 #include "weblayer/browser/browser_process.h"
+#include "weblayer/browser/feature_list_creator.h"
 #include "weblayer/browser/host_content_settings_map_factory.h"
 #include "weblayer/browser/permissions/weblayer_permissions_client.h"
 #include "weblayer/browser/stateful_ssl_host_state_delegate_factory.h"
@@ -131,6 +132,8 @@
 }
 
 void BrowserMainPartsImpl::PreMainMessageLoopRun() {
+  FeatureListCreator::GetInstance()->PerformPreMainMessageLoopStartup();
+
   // It's necessary to have a complete dependency graph of
   // BrowserContextKeyedServices before calling out to the delegate (which
   // will potentially create a profile), so that a profile creation message is
diff --git a/weblayer/browser/feature_list_creator.cc b/weblayer/browser/feature_list_creator.cc
index 2075e1c..6da30d4 100644
--- a/weblayer/browser/feature_list_creator.cc
+++ b/weblayer/browser/feature_list_creator.cc
@@ -20,6 +20,7 @@
 #include "components/prefs/pref_service_factory.h"
 #include "components/variations/pref_names.h"
 #include "components/variations/service/ui_string_overrider.h"
+#include "components/variations/service/variations_service.h"
 #include "components/variations/variations_crash_keys.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/common/content_switch_dependent_feature_overrides.h"
@@ -37,6 +38,8 @@
 namespace weblayer {
 namespace {
 
+FeatureListCreator* feature_list_creator_instance = nullptr;
+
 void HandleReadError(PersistentPrefStore::PrefReadError error) {}
 
 #if defined(OS_ANDROID)
@@ -75,9 +78,20 @@
 
 }  // namespace
 
-FeatureListCreator::FeatureListCreator() = default;
+FeatureListCreator::FeatureListCreator() {
+  DCHECK(!feature_list_creator_instance);
+  feature_list_creator_instance = this;
+}
 
-FeatureListCreator::~FeatureListCreator() = default;
+FeatureListCreator::~FeatureListCreator() {
+  feature_list_creator_instance = nullptr;
+}
+
+// static
+FeatureListCreator* FeatureListCreator::GetInstance() {
+  DCHECK(feature_list_creator_instance);
+  return feature_list_creator_instance;
+}
 
 void FeatureListCreator::SetSystemNetworkContextManager(
     SystemNetworkContextManager* system_network_context_manager) {
@@ -107,7 +121,7 @@
   DCHECK(system_network_context_manager_);
   variations_service_ = variations::VariationsService::Create(
       std::make_unique<WebLayerVariationsServiceClient>(
-          system_network_context_manager_->GetSharedURLLoaderFactory()),
+          system_network_context_manager_),
       local_state_.get(), metrics_client->metrics_state_manager(),
       switches::kDisableBackgroundNetworking, variations::UIStringOverrider(),
       base::BindOnce(&content::GetNetworkConnectionTracker));
@@ -118,7 +132,6 @@
   std::vector<std::string> variation_ids;
   auto feature_list = std::make_unique<base::FeatureList>();
 
-  variations_service_->PerformPreMainMessageLoopStartup();
   variations_service_->SetupFieldTrials(
       cc::switches::kEnableGpuBenchmarking, switches::kEnableFeatures,
       switches::kDisableFeatures, unforceable_field_trials, variation_ids,
@@ -140,4 +153,12 @@
   SetUpFieldTrials();
 }
 
+void FeatureListCreator::PerformPreMainMessageLoopStartup() {
+#if defined(OS_ANDROID)
+  // It is expected this is called after SetUpFieldTrials().
+  DCHECK(variations_service_);
+  variations_service_->PerformPreMainMessageLoopStartup();
+#endif
+}
+
 }  // namespace weblayer
diff --git a/weblayer/browser/feature_list_creator.h b/weblayer/browser/feature_list_creator.h
index 1122989..ac6af13f 100644
--- a/weblayer/browser/feature_list_creator.h
+++ b/weblayer/browser/feature_list_creator.h
@@ -8,9 +8,12 @@
 #include <memory>
 
 #include "components/prefs/pref_service.h"
-#include "components/variations/service/variations_service.h"
 #include "weblayer/browser/weblayer_field_trials.h"
 
+namespace variations {
+class VariationsService;
+}
+
 namespace weblayer {
 class SystemNetworkContextManager;
 
@@ -23,12 +26,20 @@
   FeatureListCreator();
   ~FeatureListCreator();
 
+  // Return the single instance of FeatureListCreator. This does *not* trigger
+  // creation.
+  static FeatureListCreator* GetInstance();
+
   void SetSystemNetworkContextManager(
       SystemNetworkContextManager* system_network_context_manager);
 
   // Must be called after SetSharedURLLoaderFactory.
   void CreateFeatureListAndFieldTrials();
 
+  // Called from content::BrowserMainParts::PreMainMessageLoopRun() to perform
+  // initialization necessary prior to running the main message loop.
+  void PerformPreMainMessageLoopStartup();
+
   PrefService* local_state() const { return local_state_.get(); }
 
  private:
diff --git a/weblayer/browser/java/BUILD.gn b/weblayer/browser/java/BUILD.gn
index 2712bcd6..bfd1dcf 100644
--- a/weblayer/browser/java/BUILD.gn
+++ b/weblayer/browser/java/BUILD.gn
@@ -7,7 +7,10 @@
 import("//weblayer/variables.gni")
 
 android_resources("weblayer_resources") {
-  sources = [ "res/layout/weblayer_url_bar.xml" ]
+  sources = [
+    "res/layout/weblayer_url_bar.xml",
+    "res/values/dimens.xml",
+  ]
   custom_package = "org.chromium.weblayer_private"
   deps = [
     "//components/browser_ui/strings/android:browser_ui_strings_grd",
diff --git a/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java b/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java
index 79a540c..4cbe929d 100644
--- a/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java
+++ b/weblayer/browser/java/org/chromium/weblayer_private/UrlBarControllerImpl.java
@@ -5,6 +5,7 @@
 package org.chromium.weblayer_private;
 
 import android.content.Context;
+import android.graphics.Color;
 import android.os.Bundle;
 import android.util.TypedValue;
 import android.view.View;
@@ -81,6 +82,8 @@
             super(context);
             mTextSize = options.getFloat(URL_TEXT_SIZE, DEFAULT_TEXT_SIZE);
             View.inflate(getContext(), R.layout.weblayer_url_bar, this);
+            setOrientation(LinearLayout.HORIZONTAL);
+            setBackgroundColor(Color.TRANSPARENT);
             mUrlTextView = findViewById(R.id.url_text);
             mSecurityButton = (ImageButton) findViewById(R.id.security_button);
 
diff --git a/weblayer/browser/java/res/layout/weblayer_url_bar.xml b/weblayer/browser/java/res/layout/weblayer_url_bar.xml
index 8c5af6b..858b508 100644
--- a/weblayer/browser/java/res/layout/weblayer_url_bar.xml
+++ b/weblayer/browser/java/res/layout/weblayer_url_bar.xml
@@ -3,27 +3,27 @@
      Use of this source code is governed by a BSD-style license that can be
      found in the LICENSE file. -->
 
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:background="@android:color/transparent"
-    android:orientation="horizontal">
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <!-- ContentDescription is set programmatically. -->
     <ImageButton
-        tools:ignore="ContentDescription"
         android:id="@+id/security_button"
-        android:layout_width="15dp"
-        android:layout_height="match_parent"
-        android:layout_marginLeft="5dp"
-        android:layout_marginRight="5dp"
-        android:background="@android:color/transparent" />
+        tools:ignore="ContentDescription"
+        android:background="@android:color/transparent"
+        android:layout_gravity="center_vertical"
+        android:layout_height="@dimen/security_status_icon_size"
+        android:layout_width="@dimen/security_status_icon_size"
+        android:scaleType="center"
+        android:paddingEnd="@dimen/security_status_icon_padding"/>
 
     <TextView
         android:id="@+id/url_text"
-        android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginTop="5dp"
-        android:layout_marginBottom="5dp"
-        android:gravity="center_vertical"/>
-</LinearLayout>
+        android:layout_width="0dp"
+        android:layout_weight="1"
+        android:layout_gravity="center_vertical"
+        android:maxLines="1"
+        android:paddingEnd="@dimen/url_text_edge_padding"/>
+
+</merge>
diff --git a/weblayer/browser/java/res/values/dimens.xml b/weblayer/browser/java/res/values/dimens.xml
new file mode 100644
index 0000000..0d853e2
--- /dev/null
+++ b/weblayer/browser/java/res/values/dimens.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2020 The Chromium Authors. All rights reserved.
+     Use of this source code is governed by a BSD-style license that can be
+     found in the LICENSE file. -->
+
+<resources>
+    <dimen name="security_status_icon_size">18dp</dimen>
+    <dimen name="security_status_icon_padding">5dp</dimen>
+    <dimen name="url_text_edge_padding">8dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/weblayer/browser/weblayer_variations_service_client.cc b/weblayer/browser/weblayer_variations_service_client.cc
index 95aba76f..fcb6bb5 100644
--- a/weblayer/browser/weblayer_variations_service_client.cc
+++ b/weblayer/browser/weblayer_variations_service_client.cc
@@ -8,6 +8,7 @@
 #include "base/threading/scoped_blocking_call.h"
 #include "build/build_config.h"
 #include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "weblayer/browser/system_network_context_manager.h"
 
 #if defined(OS_ANDROID)
 #include "components/version_info/android/channel_getter.h"
@@ -29,8 +30,10 @@
 }  // namespace
 
 WebLayerVariationsServiceClient::WebLayerVariationsServiceClient(
-    scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory)
-    : shared_url_loader_factory_(std::move(shared_url_loader_factory)) {}
+    SystemNetworkContextManager* network_context_manager)
+    : network_context_manager_(network_context_manager) {
+  DCHECK(network_context_manager_);
+}
 
 WebLayerVariationsServiceClient::~WebLayerVariationsServiceClient() = default;
 
@@ -41,7 +44,7 @@
 
 scoped_refptr<network::SharedURLLoaderFactory>
 WebLayerVariationsServiceClient::GetURLLoaderFactory() {
-  return shared_url_loader_factory_;
+  return network_context_manager_->GetSharedURLLoaderFactory();
 }
 
 network_time::NetworkTimeTracker*
diff --git a/weblayer/browser/weblayer_variations_service_client.h b/weblayer/browser/weblayer_variations_service_client.h
index de154732..dc35d697 100644
--- a/weblayer/browser/weblayer_variations_service_client.h
+++ b/weblayer/browser/weblayer_variations_service_client.h
@@ -11,22 +11,21 @@
 #include "base/memory/scoped_refptr.h"
 #include "components/variations/service/variations_service_client.h"
 
-namespace network {
-class SharedURLLoaderFactory;
-}  // namespace network
-
 namespace weblayer {
 
+class SystemNetworkContextManager;
+
 // WebLayerVariationsServiceClient provides an implementation of
 // VariationsServiceClient, all members are currently stubs for WebLayer.
 class WebLayerVariationsServiceClient
     : public variations::VariationsServiceClient {
  public:
   explicit WebLayerVariationsServiceClient(
-      scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory);
+      SystemNetworkContextManager* network_context_manager);
   ~WebLayerVariationsServiceClient() override;
 
  private:
+  // variations::VariationsServiceClient:
   base::OnceCallback<base::Version(void)> GetVersionForSimulationCallback()
       override;
   scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() override;
@@ -35,7 +34,7 @@
   bool OverridesRestrictParameter(std::string* parameter) override;
   bool IsEnterprise() override;
 
-  scoped_refptr<network::SharedURLLoaderFactory> shared_url_loader_factory_;
+  SystemNetworkContextManager* network_context_manager_;
 
   DISALLOW_COPY_AND_ASSIGN(WebLayerVariationsServiceClient);
 };
diff --git a/weblayer/public/java/org/chromium/weblayer/UrlBarOptions.java b/weblayer/public/java/org/chromium/weblayer/UrlBarOptions.java
index c793190..26f77b6 100644
--- a/weblayer/public/java/org/chromium/weblayer/UrlBarOptions.java
+++ b/weblayer/public/java/org/chromium/weblayer/UrlBarOptions.java
@@ -37,7 +37,7 @@
          * Sets the text size of the URL bar.
          *
          * @param textSize The desired size of the URL bar text in scalable pixels.
-         * The default is 10.0F and the minimum allowed size is 5.0F.
+         * The default is 14.0F and the minimum allowed size is 5.0F.
          */
         public Builder setTextSizeSP(float textSize) {
             mOptions.putFloat(URL_TEXT_SIZE, textSize);
diff --git a/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml b/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml
index 46578b9..4ef35b3d 100644
--- a/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml
+++ b/weblayer/shell/android/shell_apk/res/layout/shell_browser_controls.xml
@@ -14,13 +14,11 @@
 
     <ViewSwitcher
         android:id="@+id/url_view_container"
-        android:layout_marginStart="5dp"
-        android:layout_marginTop="5dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="start"
         android:layout_alignParentStart="true"
-        android:layout_toStartOf="@+id/menu_button">
+        android:layout_toStartOf="@+id/menu_button"
+        android:paddingStart="2dp">
 
         <EditText
             android:id="@+id/editable_url_view"
@@ -28,13 +26,13 @@
             android:selectAllOnFocus="true"
             android:imeOptions="actionGo"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:textSize="15sp"/>
 
         <View
             android:id="@+id/noneditable_url_view_placeholder"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"/>
+            android:layout_height="match_parent"/>
 
     </ViewSwitcher>
 
@@ -42,9 +40,10 @@
         android:id="@+id/menu_button"
         android:src="@android:drawable/ic_menu_more"
         android:background="@android:color/transparent"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="end"
+        android:layout_width="25dp"
+        android:layout_height="25dp"
+        android:scaleType="fitXY"
+        android:layout_marginTop="5dp"
         android:layout_alignParentEnd="true"/>
 
     <ProgressBar